2

API 呼び出し用のカスタム暗号化ミドルウェアを実装したいと考えています。最初に、リクエストのボディ ( IOwinContext.Request.Body) とヘッダー (Encryption-Key & Signature) を読み取ります。次に、リクエスト本文を復号化します。これにより、純粋な json 文字列が得られます。ここで、注意が必要な部分があります。この json を に書き戻したいIOwinContextRequest.Bodyので、オブジェクトに逆シリアル化し、後で Controller メソッドの引数として渡すことができます。これが私がすることです:

起動:

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.Use(typeof(EncryptionMiddleware));

        ...
    }
}

ミドルウェア:

public class EncryptionMiddleware : OwinMiddleware
{
    public EncryptionMiddleware(OwinMiddleware next) : base(next)
    {
        //
    }

    public async override Task Invoke(IOwinContext context)
    {
        var request = context.Request;
        string json = GetDecryptedJson(context);
        MemoryStream stream = new MemoryStream();
        stream.Write(json, 0, json.Length);
        request.Headers["Content-Lenght"] = json.Lenght.ToString();
        request.Body = stream;
        await Next.Invoke(context);
    }
}

今、私が得るのはこのエラーです:

System.Web.Extensions.dll!System.Web.Script.Serialization.JavaScriptObjectDeserializer.DeserializePrimitiveObject() 例外がスローされました: System.Web.Extensions.dll の 'System.ArgumentException'

Additional information: Invalid JSON primitive: 8yi9OH2JE0H0cwZ.

元の場所IOwinContext.Request.Body

8yi9OH2JE0H0cwZ/fyY5Fks4nW(...omitted...)PvL32AVRjLA==

したがって、この方法でリクエストボディを変更することはできないと想定しました。これをテストするために、ミドルウェアを次のように書き直しました。

public async override Task Invoke(IOwinContext context)
{
    var request = context.Request;

    string requestBody = new StreamReader(request.Body).ReadToEnd();
    Debug.WriteLine(requestBody); // Prints "ORIGINAL BODY"
    string newBody = "\"newBody\"";
    MemoryStream memStream = new MemoryStream(Encoding.UTF8.GetBytes(newBody));
    request.Headers["Content-Length"] = newBody.Length.ToString();
    request.Body = memStream;
    await Next.Invoke(context);
}

Controllerメソッドは「newBody」ではなく「ORIGINAL BODY」を受け取るべきだと思ったのですが、実際には次のエラーが発生しました。

System.dll!System.Diagnostics.PerformanceCounter.InitializeImpl() 例外がスローされました: System.dll の 'System.InvalidOperationException'

追加情報: 要求されたパフォーマンス カウンターはカスタム カウンターではないため、ReadOnly として初期化する必要があります。

問題は、私のアプローチの何が問題なのですか? リクエストボディを書き換える正しい方法は何ですか? 十分な回避策はありますか? ところで: データの復号化はテスト済みで完璧であるため、エラーが発生することはありません。

編集:回答/コメントする前に、TLS は既に使用されています。これは、セキュリティのもう 1 つのレイヤーです。私は車輪を再発明しているわけではありません。新しいものを追加しています。

4

2 に答える 2

5

Request.BodyOWINパイプラインでOWINの変更をテストするためのミドルウェアをいくつか作成しました

public class DecryptionMiddleWare : OwinMiddleware {
    private string expected;
    private string decryptedString;

    public DecryptionMiddleWare(OwinMiddleware next, string expected, string decryptedString)
        : base(next) {
        this.expected = expected;
        this.decryptedString = decryptedString;
    }

    public async override System.Threading.Tasks.Task Invoke(IOwinContext context) {
        await DecryptRequest(context);

        await Next.Invoke(context);
    }

    private async Task DecryptRequest(IOwinContext context) {
        var request = context.Request;
        var requestBody = new StreamReader(request.Body).ReadToEnd();
        Assert.AreEqual(expected, requestBody);
        //Fake decryption code
        if (expected == requestBody) {
            //replace request stream to downstream handlers
            var decryptedContent = new StringContent(decryptedString, Encoding.UTF8, "application/json");
            var requestStream = await decryptedContent.ReadAsStreamAsync();
            request.Body = requestStream;
        }
    }
}

public class AnotherCustomMiddleWare : OwinMiddleware {
    private string expected;
    private string responseContent;

    public AnotherCustomMiddleWare(OwinMiddleware next, string expected, string responseContent)
        : base(next) {
        this.expected = expected;
        this.responseContent = responseContent;
    }

    public async override System.Threading.Tasks.Task Invoke(IOwinContext context) {
        var request = context.Request;
        var requestBody = new StreamReader(request.Body).ReadToEnd();

        Assert.AreEqual(expected, requestBody);

        var owinResponse = context.Response;
        // hold on to original stream
        var owinResponseStream = owinResponse.Body;
        //buffer the response stream in order to intercept downstream writes
        var responseBuffer = new MemoryStream();
        owinResponse.Body = responseBuffer;

        await Next.Invoke(context);

        if (expected == requestBody) {
            owinResponse.ContentType = "text/plain";
            owinResponse.StatusCode = (int)HttpStatusCode.OK;
            owinResponse.ReasonPhrase = HttpStatusCode.OK.ToString();

            var customResponseBody = new StringContent(responseContent);
            var customResponseStream = await customResponseBody.ReadAsStreamAsync();
            await customResponseStream.CopyToAsync(owinResponseStream);

            owinResponse.ContentLength = customResponseStream.Length;
            owinResponse.Body = owinResponseStream;
        }

    }
}

次に、インメモリ OWIN 統合テストを作成して、データがミドルウェアをどのように通過するかを確認し、正しいデータが受信されていることをテストしました。

[TestMethod]
public async Task Change_OWIN_Request_Body_Test() {
    var encryptedContent = "Hello World";
    var expectedResponse = "I am working";

    using (var server = TestServer.Create<Startup1>()) {

        var content = new StringContent(encryptedContent);
        var response = await server.HttpClient.PostAsync("/", content);
        var result = await response.Content.ReadAsStringAsync();

        Assert.AreEqual(expectedResponse, result);
    }
}

public class Startup1 {
    public void Configuration(IAppBuilder appBuilder) {
        var encryptedContent = "Hello World";
        var decryptedString = "Hello OWIN";
        var expectedResponse = "I am working";
        appBuilder.Use<DecryptionMiddleWare>(encryptedContent, decryptedString);
        appBuilder.Use<AnotherCustomMiddleWare>(decryptedString, expectedResponse);
    }
}

OWIN パイプラインを介してデータを渡すことができることを証明するテストに合格しました。

わかりました。次に、Web API で動作するかどうかを確認したいと思いました。テストAPIコントローラーを作成しました

public class TestController : ApiController {
    [HttpPost]
    public IHttpActionResult Post([FromBody]string input) {
        if (input == "Hello From OWIN")
            return Ok("I am working");

        return NotFound();
    }
}

そして、Web API とカスタム復号化ミドルウェアを使用するように新しいスタートアップを構成しました。

public class Startup2 {
    public void Configuration(IAppBuilder appBuilder) {
        var encryptedContent = "Hello World";
        var decryptedString = "\"Hello From OWIN\"";
        appBuilder.Use<DecryptionMiddleWare>(encryptedContent, decryptedString);

        //Configure Web API middleware
        var config = new HttpConfiguration();
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        appBuilder.UseWebApi(config);
    }
}

これがインメモリ統合テストです

[TestMethod]
public async Task Change_OWIN_Request_Body_To_WebApi_Test() {
    var encryptedContent = "Hello World";
    var expectedResponse = "\"I am working\"";

    using (var server = TestServer.Create<Startup2>()) {

        var content = new StringContent(encryptedContent, Encoding.UTF8, "application/json");
        var response = await server.HttpClient.PostAsync("api/Test", content);
        var result = await response.Content.ReadAsStringAsync();

        Assert.AreEqual(expectedResponse, result);
    }
}

これも合格しました。

上記のサンプル コードを見て、質問の例のどこで問題が発生したかについての洞察が得られるかどうかを確認してください。

また、Web API ミドルウェアの前に、パイプラインの早い段階でカスタム ミドルウェアを配置することを忘れないでください。

それが役に立てば幸い

于 2016-10-03T00:43:09.380 に答える
4

私はずっと前にこの問題を解決しましたが、@Nkosi のアドバイスの後、解決策を投稿しています。

私がしたことは、回避策、またはミドルウェアからアクションフィルターへの「ブリッジ」です。コードは次のとおりです。

ミドルウェア

public class EncryptionMiddleware : OwinMiddleware
{

    public EncryptionMiddleware(OwinMiddleware next) : base(next)
    {
        //
    }

    public async override Task Invoke(IOwinContext context)
    {
        var request = context.Request;

        string requestBody = new StreamReader(request.Body).ReadToEnd();

        var obj = // do your work here
        System.Web.HttpContext.Current.Items[OBJECT_ITEM_KEY] = obj;
        await Next.Invoke(context);
        return;
    }
}

フィルター

public class EncryptedParameter : ActionFilterAttribute, IActionFilter
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var obj = HttpContext.Current.Items[OBJECT_ITEM_KEY];
        HttpContext.Current.Items.Remove(AppConfig.ITEM_DATA_KEY);

        if (filterContext.ActionParameters.ContainsKey("data"))
            filterContext.ActionParameters["data"] = obj;
    }
}

コントローラ

public class MyController : Controller
{
    [HttpPost]
    [EncryptedParameter]
    public JsonResult MyMethod(MyObject data)
    {
        // your logic here
    }
}
于 2017-02-08T09:16:42.020 に答える