ASP.NET वेब API को कैसे सुरक्षित करें [बंद]


397

मैं ASP.NET वेब एपीआई का उपयोग करके एक RESTful वेब सेवा का निर्माण करना चाहता हूं जो तृतीय-पक्ष डेवलपर्स मेरे एप्लिकेशन के डेटा तक पहुंचने के लिए उपयोग करेंगे।

मैंने OAuth के बारे में काफी पढ़ा है और यह मानक प्रतीत होता है, लेकिन प्रलेखन के साथ एक अच्छा नमूना खोजना यह बताते हुए कि यह कैसे काम करता है (और यह वास्तव में काम करता है!) अविश्वसनीय रूप से मुश्किल लगता है (विशेषकर OAuth के लिए एक नौसिखिया के लिए)।

क्या एक नमूना है जो वास्तव में बनाता है और काम करता है और दिखाता है कि इसे कैसे लागू किया जाए?

मैंने कई नमूने डाउनलोड किए हैं:

  • DotNetOAuth - प्रलेखन एक नौसिखिया परिप्रेक्ष्य से निराशाजनक है
  • थिंकटेक्चर - इसे बनाने के लिए नहीं मिल सकता है

मैंने एक सरल टोकन-आधारित योजना का सुझाव देने वाले ब्लॉगों को भी देखा है ( इस तरह ) - यह पहिया को फिर से आविष्कार करने जैसा लगता है, लेकिन इसमें वैचारिक रूप से काफी सरल होने का फायदा है।

ऐसा लगता है कि एसओ पर इस तरह के कई सवाल हैं लेकिन कोई अच्छा जवाब नहीं है।

इस स्पेस में हर कोई क्या कर रहा है?

जवाबों:


292

अपडेट करें:

मैंने अपने अन्य उत्तर में यह लिंक जोड़ा है कि कैसे JWT में रुचि रखने वाले किसी भी व्यक्ति के लिए ASP.NET वेब एपीआई के लिए JWT प्रमाणीकरण का उपयोग करें।


हम वेब एपीआई को सुरक्षित करने के लिए HMAC प्रमाणीकरण लागू करने में कामयाब रहे हैं, और यह ठीक काम किया है। एचएमएसी प्रमाणीकरण प्रत्येक उपभोक्ता के लिए एक गुप्त कुंजी का उपयोग करता है जो उपभोक्ता और सर्वर दोनों को एक संदेश हैश करने के लिए जानते हैं, एचएमएसी 256 का उपयोग किया जाना चाहिए। अधिकांश मामलों में, उपभोक्ता का हैशेड पासवर्ड एक गुप्त कुंजी के रूप में उपयोग किया जाता है।

संदेश सामान्यतः HTTP अनुरोध में डेटा से बनाया गया है, या यहां तक ​​कि अनुकूलित डेटा जो HTTP हेडर में जोड़ा गया है, संदेश में शामिल हो सकता है:

  1. टाइमस्टैम्प: अनुरोध भेजने का समय (UTC या GMT)
  2. HTTP वर्ब: GET, POST, PUT, DELETE।
  3. पोस्ट डेटा और क्वेरी स्ट्रिंग,
  4. यूआरएल

हुड के तहत, एचएमएसी प्रमाणीकरण होगा:

उपभोक्ता हस्ताक्षर (hmac हैश का आउटपुट), HTTP अनुरोध के टेम्पलेट के निर्माण के बाद, वेब सर्वर पर एक HTTP अनुरोध भेजता है:

User-Agent: {agent}   
Host: {host}   
Timestamp: {timestamp}
Authentication: {username}:{signature}

GET अनुरोध के लिए उदाहरण:

GET /webapi.hmac/api/values

User-Agent: Fiddler    
Host: localhost    
Timestamp: Thursday, August 02, 2012 3:30:32 PM 
Authentication: cuongle:LohrhqqoDy6PhLrHAXi7dUVACyJZilQtlDzNbLqzXlw=

हस्ताक्षर प्राप्त करने के लिए हैश करने का संदेश:

GET\n
Thursday, August 02, 2012 3:30:32 PM\n
/webapi.hmac/api/values\n

क्वेरी स्ट्रिंग के साथ POST अनुरोध के लिए उदाहरण (नीचे हस्ताक्षर सही नहीं है, सिर्फ एक उदाहरण)

POST /webapi.hmac/api/values?key2=value2

User-Agent: Fiddler    
Host: localhost    
Content-Type: application/x-www-form-urlencoded
Timestamp: Thursday, August 02, 2012 3:30:32 PM 
Authentication: cuongle:LohrhqqoDy6PhLrHAXi7dUVACyJZilQtlDzNbLqzXlw=

key1=value1&key3=value3

हस्ताक्षर प्राप्त करने के लिए हैश करने का संदेश

GET\n
Thursday, August 02, 2012 3:30:32 PM\n
/webapi.hmac/api/values\n
key1=value1&key2=value2&key3=value3

कृपया ध्यान दें कि फॉर्म डेटा और क्वेरी स्ट्रिंग क्रम में होनी चाहिए, इसलिए सर्वर पर कोड को क्वेरी स्ट्रिंग और सही संदेश बनाने के लिए डेटा फॉर्म मिलता है।

जब HTTP अनुरोध सर्वर पर आता है, तो सूचना प्राप्त करने के लिए अनुरोध को पार्स करने के लिए एक प्रमाणीकरण क्रिया फ़िल्टर लागू किया जाता है: HTTP क्रिया, टाइमस्टैम्प, uri, फॉर्म डेटा और क्वेरी स्ट्रिंग, फिर गुप्त के साथ हस्ताक्षर (hmac हैश का उपयोग करने के लिए) पर आधारित कुंजी (हैशेड पासवर्ड) सर्वर पर।

गुप्त कुंजी डेटाबेस से उपयोगकर्ता नाम के साथ अनुरोध पर मिलती है।

फिर सर्वर कोड निर्मित हस्ताक्षर के साथ अनुरोध पर हस्ताक्षर की तुलना करता है; यदि बराबर है, तो प्रमाणीकरण पारित किया जाता है, अन्यथा, यह विफल रहा।

हस्ताक्षर बनाने का कोड:

private static string ComputeHash(string hashedPassword, string message)
{
    var key = Encoding.UTF8.GetBytes(hashedPassword.ToUpper());
    string hashString;

    using (var hmac = new HMACSHA256(key))
    {
        var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(message));
        hashString = Convert.ToBase64String(hash);
    }

    return hashString;
}

तो, रिप्ले हमले को कैसे रोकें?

टाइमस्टैम्प के लिए बाधा जोड़ें, कुछ इस तरह:

servertime - X minutes|seconds  <= timestamp <= servertime + X minutes|seconds 

(सर्वरटाइम: सर्वर पर आने वाले अनुरोध का समय)

और, मेमोरी में अनुरोध के हस्ताक्षर को कैश करें (मेमोरीकाच का उपयोग करें, समय की सीमा में रखना चाहिए)। यदि अगला अनुरोध पिछले अनुरोध के साथ समान हस्ताक्षर के साथ आता है, तो इसे अस्वीकार कर दिया जाएगा।

डेमो कोड यहां दिया गया है: https://github.com/cuongle/Hmac.WebApi


2
@ नाम: केवल टाइमस्टैम्प पर्याप्त नहीं लगता है, कम समय के दौरान वे अनुरोध का अनुकरण कर सकते हैं और सर्वर को भेजा जा सकता है, मैंने अभी अपनी पोस्ट संपादित की है, दोनों का उपयोग करना सबसे अच्छा होगा।
congongle

1
क्या आपको यकीन है कि यह काम कर रहा है जैसा कि इसे करना चाहिए? आप संदेश के साथ टाइमस्टैम्प पर हैं और उस संदेश को कैशिंग कर रहे हैं। इसका मतलब होगा कि एक अलग हस्ताक्षर प्रत्येक अनुरोध जो आपके कैश्ड हस्ताक्षर को बेकार कर देगा।
फिलिप स्टास

1
@ फ़िलस्टैस्टस: लगता है मुझे आपकी बात समझ में नहीं आ रही है, यहाँ कैशे का इस्तेमाल करने का कारण रिले अटैक को रोकना है, और कुछ नहीं
congongle

1
@ क्रिस: आप इस पृष्ठ को देख सकते हैं ( jokecamp.wordpress.com/2012/10/21/… )। मैं जल्द ही इस स्रोत को अपडेट
करूंगा

1
समाधान काम करने का सुझाव देता है, लेकिन आप मैन-इन-द-मिडिल हमले को नहीं रोक सकते हैं, इसके लिए आपको एचटीटीपीएस को लागू करना होगा
रिफैक्टर

34

मैं सबसे पहले सीधे समाधान के साथ शुरू करने का सुझाव दूंगा - शायद सरल HTTP बेसिक प्रमाणीकरण + HTTPS आपके परिदृश्य में पर्याप्त है।

यदि नहीं (उदाहरण के लिए आप https का उपयोग नहीं कर सकते हैं, या अधिक जटिल कुंजी प्रबंधन की आवश्यकता है), तो आप दूसरों के सुझाव के अनुसार HMAC- आधारित समाधान देख सकते हैं। ऐसे API का एक अच्छा उदाहरण Amazon S3 ( http://s3.amazonaws.com/doc/s3-developer-guide/RESTAuthentication.html ) होगा

मैंने ASP.NET वेब एपीआई में एचएमएसी आधारित प्रमाणीकरण के बारे में एक ब्लॉग पोस्ट लिखा था। यह वेब एपीआई सेवा और वेब एपीआई क्लाइंट दोनों पर चर्चा करता है और कोड बिटबकैट पर उपलब्ध है। http://www.piotrwalat.net/hmac-authentication-in-asp-net-web-api/

यहाँ वेब एपीआई में बुनियादी प्रमाणीकरण के बारे में एक पोस्ट है: http://www.piotrwalat.net/basic-http-authentication-in-asp-net-web-api-use-message-handlers/

याद रखें कि यदि आप तृतीय पक्ष को एपीआई प्रदान करने जा रहे हैं, तो आप ग्राहक पुस्तकालयों को वितरित करने के लिए सबसे अधिक जिम्मेदार होंगे। बुनियादी प्रमाणीकरण का यहाँ एक महत्वपूर्ण लाभ है क्योंकि यह बॉक्स से बाहर अधिकांश प्रोग्रामिंग प्लेटफार्मों पर समर्थित है। दूसरी ओर, HMAC, यह मानकीकृत नहीं है और इसके लिए कस्टम कार्यान्वयन की आवश्यकता होगी। ये अपेक्षाकृत सीधे होने चाहिए, लेकिन फिर भी काम की आवश्यकता होती है।

पुनश्च। HTTPS + प्रमाणपत्र का उपयोग करने का एक विकल्प भी है। http://www.piotrwalat.net/client-certificate-authentication-in-asp-net-web-api-and-windows-store-apps/


23

क्या आपने DevDefined.OAuth की कोशिश की है?

मैंने इसका उपयोग 2-पैर वाले OAuth के साथ अपनी WebApi को सुरक्षित करने के लिए किया है। मैंने इसे PHP क्लाइंट के साथ सफलतापूर्वक टेस्ट भी किया है।

इस लाइब्रेरी का उपयोग करके OAuth के लिए समर्थन जोड़ना काफी आसान है। यहां आप ASP.NET MVC वेब API के लिए प्रदाता को कैसे कार्यान्वित कर सकते हैं:

1) DevDefined.OAuth का स्रोत कोड प्राप्त करें: https://github.com/bittercoder/DevDefined.OAuth - नवीनतम संस्करण के लिए अनुमति देता हैOAuthContextBuilder एक्स्टेंसिबिलिटी के ।

2) लाइब्रेरी का निर्माण करें और इसे अपने वेब एपीआई प्रोजेक्ट में देखें।

3) एक संदर्भ बनाने से समर्थन करने के लिए एक कस्टम संदर्भ बिल्डर बनाएँ HttpRequestMessage:

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net.Http;
using System.Web;

using DevDefined.OAuth.Framework;

public class WebApiOAuthContextBuilder : OAuthContextBuilder
{
    public WebApiOAuthContextBuilder()
        : base(UriAdjuster)
    {
    }

    public IOAuthContext FromHttpRequest(HttpRequestMessage request)
    {
        var context = new OAuthContext
            {
                RawUri = this.CleanUri(request.RequestUri), 
                Cookies = this.CollectCookies(request), 
                Headers = ExtractHeaders(request), 
                RequestMethod = request.Method.ToString(), 
                QueryParameters = request.GetQueryNameValuePairs()
                    .ToNameValueCollection(), 
            };

        if (request.Content != null)
        {
            var contentResult = request.Content.ReadAsByteArrayAsync();
            context.RawContent = contentResult.Result;

            try
            {
                // the following line can result in a NullReferenceException
                var contentType = 
                    request.Content.Headers.ContentType.MediaType;
                context.RawContentType = contentType;

                if (contentType.ToLower()
                    .Contains("application/x-www-form-urlencoded"))
                {
                    var stringContentResult = request.Content
                        .ReadAsStringAsync();
                    context.FormEncodedParameters = 
                        HttpUtility.ParseQueryString(stringContentResult.Result);
                }
            }
            catch (NullReferenceException)
            {
            }
        }

        this.ParseAuthorizationHeader(context.Headers, context);

        return context;
    }

    protected static NameValueCollection ExtractHeaders(
        HttpRequestMessage request)
    {
        var result = new NameValueCollection();

        foreach (var header in request.Headers)
        {
            var values = header.Value.ToArray();
            var value = string.Empty;

            if (values.Length > 0)
            {
                value = values[0];
            }

            result.Add(header.Key, value);
        }

        return result;
    }

    protected NameValueCollection CollectCookies(
        HttpRequestMessage request)
    {
        IEnumerable<string> values;

        if (!request.Headers.TryGetValues("Set-Cookie", out values))
        {
            return new NameValueCollection();
        }

        var header = values.FirstOrDefault();

        return this.CollectCookiesFromHeaderString(header);
    }

    /// <summary>
    /// Adjust the URI to match the RFC specification (no query string!!).
    /// </summary>
    /// <param name="uri">
    /// The original URI. 
    /// </param>
    /// <returns>
    /// The adjusted URI. 
    /// </returns>
    private static Uri UriAdjuster(Uri uri)
    {
        return
            new Uri(
                string.Format(
                    "{0}://{1}{2}{3}", 
                    uri.Scheme, 
                    uri.Host, 
                    uri.IsDefaultPort ?
                        string.Empty :
                        string.Format(":{0}", uri.Port), 
                    uri.AbsolutePath));
    }
}

4) OAuth प्रदाता बनाने के लिए इस ट्यूटोरियल का उपयोग करें: http://code.google.com/p/devdefined-tools/wiki/OAuthProvider । अंतिम चरण में (संरक्षित संसाधन उदाहरण तक पहुँच) आप इस कोड का उपयोग अपनी AuthorizationFilterAttributeविशेषता में कर सकते हैं :

public override void OnAuthorization(HttpActionContext actionContext)
{
    // the only change I made is use the custom context builder from step 3:
    OAuthContext context = 
        new WebApiOAuthContextBuilder().FromHttpRequest(actionContext.Request);

    try
    {
        provider.AccessProtectedResourceRequest(context);

        // do nothing here
    }
    catch (OAuthException authEx)
    {
        // the OAuthException's Report property is of the type "OAuthProblemReport", it's ToString()
        // implementation is overloaded to return a problem report string as per
        // the error reporting OAuth extension: http://wiki.oauth.net/ProblemReporting
        actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
            {
               RequestMessage = request, ReasonPhrase = authEx.Report.ToString()
            };
    }
}

मैंने अपना स्वयं का प्रदाता लागू कर दिया है, इसलिए मैंने उपरोक्त कोड (बेशक WebApiOAuthContextBuilderजो मैं अपने प्रदाता में उपयोग कर रहा हूं) को छोड़कर परीक्षण नहीं किया है, लेकिन यह ठीक काम करना चाहिए।


धन्यवाद - मैं इस पर एक नज़र डालूंगा, हालांकि अभी के लिए मैंने अपना एचएमएसी-आधारित समाधान रोल किया है।
क्रेग शीयर

1
@CraigShearer - हाय, आप कहते हैं कि आपने अपना रोल कर लिया है .. बस कुछ सवाल थे अगर आपको साझा करने में कोई आपत्ति नहीं है। मैं एक समान स्थिति में हूं, जहां मेरे पास अपेक्षाकृत छोटा MVC वेब एपीआई है। एपीआई नियंत्रक अन्य नियंत्रक / क्रियाओं के साथ बैठते हैं जो प्रपत्र के अंतर्गत हैं। OAuth को लागू करना एक ओवरकिल लगता है जब मेरे पास पहले से ही एक सदस्यता प्रदाता है जिसका मैं उपयोग कर सकता हूं और मुझे केवल कुछ मुट्ठी भर संचालन को सुरक्षित करने की आवश्यकता है। मैं वास्तव में एक दूसरी कार्रवाई चाहता हूं जो एक एन्क्रिप्टेड टोकन लौटाता है - फिर बाद की कॉल में टोकन का उपयोग किया? मौजूदा सूचना समाधान को लागू करने से पहले किसी भी जानकारी का स्वागत है। धन्यवाद!
सांबोमार्टिन

@Maksymilian Majer - आप किसी भी अवसर को साझा कर सकते हैं कि आपने प्रदाता को और अधिक विस्तार से कैसे लागू किया है? मुझे क्लाइंट को प्रतिक्रियाएँ भेजने में कुछ समस्याएँ हो रही हैं।
जॉरलिन

21

वेब एपीआई ने [Authorize]सुरक्षा प्रदान करने के लिए एक विशेषता शुरू की । यह विश्व स्तर पर स्थापित किया जा सकता है (global.asx)

public static void Register(HttpConfiguration config)
{
    config.Filters.Add(new AuthorizeAttribute());
}

या प्रति नियंत्रक:

[Authorize]
public class ValuesController : ApiController{
...

निश्चित रूप से आपके प्रमाणीकरण का प्रकार भिन्न हो सकता है और आप अपना स्वयं का प्रमाणीकरण करना चाहते हैं, जब ऐसा होता है तो आपको प्राधिकृत गुण से उपयोगी विरासत मिल सकती है और अपनी आवश्यकताओं को पूरा करने के लिए इसका विस्तार किया जा सकता है:

public class DemoAuthorizeAttribute : AuthorizeAttribute
{
    public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
        if (Authorize(actionContext))
        {
            return;
        }
        HandleUnauthorizedRequest(actionContext);
    }

    protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
        var challengeMessage = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
        challengeMessage.Headers.Add("WWW-Authenticate", "Basic");
        throw new HttpResponseException(challengeMessage);
    }

    private bool Authorize(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
        try
        {
            var someCode = (from h in actionContext.Request.Headers where h.Key == "demo" select h.Value.First()).FirstOrDefault();
            return someCode == "myCode";
        }
        catch (Exception)
        {
            return false;
        }
    }
}

और अपने नियंत्रक में:

[DemoAuthorize]
public class ValuesController : ApiController{

यहाँ WebApi प्राधिकरणों के लिए अन्य कस्टम कार्यान्वयन पर एक लिंक दिया गया है:

http://www.piotrwalat.net/basic-http-authentication-in-asp-net-web-api-using-membership-provider/


@Dalorzo उदाहरण के लिए धन्यवाद, लेकिन मेरे पास कुछ मुद्दे हैं। मैंने संलग्न लिंक को देखा, लेकिन निर्देशों का पालन करना काफी काम नहीं करता है। मुझे आवश्यक जानकारी भी नहीं मिली। सबसे पहले, जब मैं नई परियोजना बनाता हूं, तो क्या प्रमाणीकरण के लिए व्यक्तिगत उपयोगकर्ता खाते चुनना सही है? या मैं इसे बिना किसी प्रमाणीकरण के छोड़ देता हूं। मुझे उल्लिखित 302 त्रुटि भी नहीं मिल रही है, लेकिन मुझे 401 त्रुटि मिल रही है। अंत में, मैं अपने दृष्टिकोण से नियंत्रक को आवश्यक जानकारी कैसे दे सकता हूं? मेरा अजाक्स कॉल कैसा दिखना चाहिए? Btw, मैं अपने MVC विचारों के लिए प्रपत्र प्रमाणीकरण का उपयोग कर रहा हूँ। क्या यही समस्या है?
अमांडा

यह काल्पनिक रूप से काम कर रहा है। बस सीखने और हमारी अपनी पहुंच टोकन पर काम करना शुरू करने के लिए अच्छा है।
कोडनेम 47

एक छोटी टिप्पणी - सावधान रहें AuthorizeAttribute, क्योंकि अलग-अलग नामस्थानों में एक ही नाम के साथ दो अलग-अलग वर्ग हैं: 1. System.Web.Mvc.AuthorizeAttribute -> MVC नियंत्रकों के लिए 2. System.Web.Http.AuthorizeAttribute -> WebApi के लिए।
विटालि मार्किटानोव

5

यदि आप सर्वर एपीआई टू सर्वर फैशन (2 लेग्ड प्रमाणीकरण के लिए वेबसाइट पर कोई पुनर्निर्देशन नहीं करना चाहते) में अपने एपीआई को सुरक्षित करना चाहते हैं। आप OAuth2 क्लाइंट क्रेडेंशियल ग्रांट प्रोटोकॉल को देख सकते हैं।

https://dev.twitter.com/docs/auth/application-only-auth

मैंने एक पुस्तकालय विकसित किया है जो आपके वेबएपीआई में इस तरह के समर्थन को आसानी से जोड़ने में आपकी सहायता कर सकता है। आप इसे NuGet पैकेज के रूप में स्थापित कर सकते हैं:

https://nuget.org/packages/OAuth2ClientCredentialsGrant/1.0.0.0

पुस्तकालय .NET .NET 4.5 को लक्षित करता है।

एक बार जब आप अपने प्रोजेक्ट में पैकेज जोड़ते हैं, तो यह आपके प्रोजेक्ट के रूट में एक रीडमी फ़ाइल बनाएगा। इस पैकेज को कॉन्फ़िगर / उपयोग करने के तरीके को देखने के लिए आप उस रीडमी फ़ाइल को देख सकते हैं।

चीयर्स!


5
क्या आप इस सोर्स के लिए ओपन सोर्स के रूप में सोर्स कोड साझा / प्रदान कर रहे हैं?
बैरीपिकर

JFR: पहला लिंक टूटा हुआ है और NuGet पैकेज को कभी अद्यतन नहीं किया गया
पेट क्यूयूम

3

@ Cuong Le के जवाब को जारी रखते हुए, रिप्ले हमले को रोकने के लिए मेरा दृष्टिकोण होगा

// साझा निजी कुंजी (या उपयोगकर्ता के पासवर्ड) का उपयोग करके क्लाइंट पक्ष पर यूनिक्स समय को एन्क्रिप्ट करें

// इसे सर्वर के लिए अनुरोध हेडर के रूप में भेजें (वेब ​​एपीआई)

// साझा निजी कुंजी (या उपयोगकर्ता के पासवर्ड) का उपयोग करके सर्वर (WEB एपीआई) पर यूनिक्स समय को डिक्रिप्ट करें

// क्लाइंट के यूनिक्स समय और सर्वर के यूनिक्स समय के बीच के समय के अंतर की जांच करें, एक्स सेकंड से अधिक नहीं होनी चाहिए

// अगर यूजर आईडी / हैश पासवर्ड सही है और डिक्रिप्टेड यूनिक्सटाइम सर्वर समय के x सेकंड के भीतर है तो यह एक वैध अनुरोध है

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.