Merge pull request #19 from NicolasConstant/topic_better-validation

Topic better validation
This commit is contained in:
Nicolas Constant 2020-12-28 06:50:32 +01:00 committed by GitHub
commit 79ae28acae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 51 additions and 27 deletions

View file

@ -90,7 +90,7 @@ namespace BirdsiteLive.Domain
var date = DateTime.UtcNow.ToUniversalTime();
var httpDate = date.ToString("r");
var digest = ComputeSha256Hash(json);
var digest = _cryptoService.ComputeSha256Hash(json);
var signature = _cryptoService.SignAndGetSignatureHeader(date, actorUrl, targetHost, digest, usedInbox);
@ -113,15 +113,6 @@ namespace BirdsiteLive.Domain
return response.StatusCode;
}
static string ComputeSha256Hash(string rawData)
{
// Create a SHA256
using (SHA256 sha256Hash = SHA256.Create())
{
// ComputeHash - returns byte array
byte[] bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(rawData));
return Convert.ToBase64String(bytes);
}
}
}
}

View file

@ -1,4 +1,5 @@
using System;
using System.Security.Cryptography;
using System.Text;
using BirdsiteLive.Domain.Factories;
@ -8,6 +9,7 @@ namespace BirdsiteLive.Domain
{
string GetUserPem(string id);
string SignAndGetSignatureHeader(DateTime date, string actor, string host, string digest, string inbox);
string ComputeSha256Hash(string data);
}
public class CryptoService : ICryptoService
@ -49,5 +51,16 @@ namespace BirdsiteLive.Domain
var header = "keyId=\"" + actor + "\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest\",signature=\"" + sig64 + "\"";
return header;
}
public string ComputeSha256Hash(string data)
{
// Create a SHA256
using (SHA256 sha256Hash = SHA256.Create())
{
// ComputeHash - returns byte array
byte[] bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(data));
return Convert.ToBase64String(bytes);
}
}
}
}

View file

@ -18,8 +18,8 @@ namespace BirdsiteLive.Domain
public interface IUserService
{
Actor GetUser(TwitterUser twitterUser);
Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity);
Task<bool> UndoFollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityUndoFollow activity);
Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity, string body);
Task<bool> UndoFollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityUndoFollow activity, string body);
}
public class UserService : IUserService
@ -79,10 +79,10 @@ namespace BirdsiteLive.Domain
return user;
}
public async Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity)
public async Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity, string body)
{
// Validate
var sigValidation = await ValidateSignature(activity.actor, signature, method, path, queryString, requestHeaders);
var sigValidation = await ValidateSignature(activity.actor, signature, method, path, queryString, requestHeaders, body);
if (!sigValidation.SignatureIsValidated) return false;
// Save Follow in DB
@ -130,10 +130,10 @@ namespace BirdsiteLive.Domain
}
public async Task<bool> UndoFollowRequestedAsync(string signature, string method, string path, string queryString,
Dictionary<string, string> requestHeaders, ActivityUndoFollow activity)
Dictionary<string, string> requestHeaders, ActivityUndoFollow activity, string body)
{
// Validate
var sigValidation = await ValidateSignature(activity.actor, signature, method, path, queryString, requestHeaders);
var sigValidation = await ValidateSignature(activity.actor, signature, method, path, queryString, requestHeaders, body);
if (!sigValidation.SignatureIsValidated) return false;
// Save Follow in DB
@ -162,8 +162,22 @@ namespace BirdsiteLive.Domain
return result == HttpStatusCode.Accepted;
}
private async Task<SignatureValidationResult> ValidateSignature(string actor, string rawSig, string method, string path, string queryString, Dictionary<string, string> requestHeaders)
private async Task<SignatureValidationResult> ValidateSignature(string actor, string rawSig, string method, string path, string queryString, Dictionary<string, string> requestHeaders, string body)
{
//Check Date Validity
var date = requestHeaders["date"];
var d = DateTime.Parse(date).ToUniversalTime();
var now = DateTime.UtcNow;
var delta = Math.Abs((d - now).TotalSeconds);
if (delta > 30) return new SignatureValidationResult { SignatureIsValidated = false };
//Check Digest
var digest = requestHeaders["digest"];
var digestHash = digest.Split(new [] {"SHA-256="},StringSplitOptions.RemoveEmptyEntries).LastOrDefault();
var calculatedDigestHash = _cryptoService.ComputeSha256Hash(body);
if (digestHash != calculatedDigestHash) return new SignatureValidationResult { SignatureIsValidated = false };
//Check Signature
var signatures = rawSig.Split(',');
var signature_header = new Dictionary<string, string>();
foreach (var signature in signatures)

View file

@ -4,6 +4,7 @@
<TargetFramework>netcoreapp3.1</TargetFramework>
<UserSecretsId>d21486de-a812-47eb-a419-05682bb68856</UserSecretsId>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<Version>0.1.0</Version>
</PropertyGroup>
<ItemGroup>

View file

@ -111,7 +111,7 @@ namespace BirdsiteLive.Controllers
case "Follow":
{
var succeeded = await _userService.FollowRequestedAsync(signature, r.Method, r.Path,
r.QueryString.ToString(), RequestHeaders(r.Headers), activity as ActivityFollow);
r.QueryString.ToString(), RequestHeaders(r.Headers), activity as ActivityFollow, body);
if (succeeded) return Accepted();
else return Unauthorized();
}
@ -119,7 +119,7 @@ namespace BirdsiteLive.Controllers
if (activity is ActivityUndoFollow)
{
var succeeded = await _userService.UndoFollowRequestedAsync(signature, r.Method, r.Path,
r.QueryString.ToString(), RequestHeaders(r.Headers), activity as ActivityUndoFollow);
r.QueryString.ToString(), RequestHeaders(r.Headers), activity as ActivityUndoFollow, body);
if (succeeded) return Accepted();
else return Unauthorized();
}

View file

@ -50,6 +50,8 @@ namespace BirdsiteLive.Controllers
[Route("/nodeinfo/{id}.json")]
public IActionResult NodeInfo(string id)
{
var version = System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString(3);
if (id == "2.0")
{
var nodeInfo = new NodeInfoV20
@ -66,7 +68,7 @@ namespace BirdsiteLive.Controllers
software = new Software()
{
name = "birdsitelive",
version = "0.1.0"
version = version
},
protocols = new[]
{
@ -101,7 +103,7 @@ namespace BirdsiteLive.Controllers
software = new SoftwareV21()
{
name = "birdsitelive",
version = "0.1.0",
version = version,
repository = "https://github.com/NicolasConstant/BirdsiteLive"
},
protocols = new[]

View file

@ -36,11 +36,14 @@
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
<a href="https://github.com/NicolasConstant/BirdsiteLive">Github</a> @*<a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>*@
</div>
</footer>
<footer class="border-top footer text-muted">
<div class="container">
<a href="https://github.com/NicolasConstant/BirdsiteLive">Github</a> @*<a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>*@
<span style="float: right;">BirdsiteLIVE @System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString(3)</span>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>