2013-05-16 44 views
0

我在其他Web服務領域是全新的,並且我在Web上嘗試了大量文章,試圖在ASP.NET中實現REST web服務(並行地在另一個webform應用程序中項目)如何使用JQUERY客戶端調用來保護ASP.NET REST

我想知道WebServices有多安全? 我設置的教程在這裏解釋:http://www.codeproject.com/Articles/128478/Consuming-WCF-REST-Services-Using-jQuery-AJAX-Call

我可以用jquery調用我的webservices,但沒有安全性。

我必須裝上安全的HTTP標頭嗎?如果是這樣,怎麼樣?

我想知道的另一件事:一個看起來html頁面源代碼的人很容易找到「passkey」或「login」/「passowrd」no?

需要你的幫助:(謝謝

編輯:

我用這個代碼調用我的REST服務使用jQuery:

function setHeader(xhr) { 
    var secretkey = "userid:uCMfSzkjue+HSDygYB5aEg=="; 
    var hashedUrl = secretkey; 
    var hashedUrlBase64 = hashedUrl.toString(); 
    xhr.setRequestHeader('Authorization', hashedUrlBase64, "1234dgt"); 
} 

$.ajax({ 
    cache: false, 
    type: "GET", 
    async: false, 
    url: rootWS + "ListeService/GetListeSerie" + "(" + listeId + ")", 
    //data: "{'id':'" + listeId + "'}", 
    contentType: "application/json; charset=utf-8", 
    dataType: "json", 
    success: function (response) { 
       ... 
    }, 
    error: function (msg) { 
     ... 
    }, 
    beforeSend: setHeader 

}); 

與此代碼,以我的REST WS:

[WebInvoke(Method = "GET", UriTemplate = "GetListeSerie({id})")] 
public List<SeriePourListe> GetListeSerie(string id) 
{ 
    if (!Security.AuthenticateUser()) 
    { 
     WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.Unauthorized; 
     return null; 
    } 

    //do some stuff 
} 

public static class Security 
{ 
    public static bool AuthenticateUser() 
    { 
     WebOperationContext ctx = WebOperationContext.Current; 
     string requestUri = ctx.IncomingRequest.UriTemplateMatch.RequestUri.ToString(); 
     string authHeader = ctx.IncomingRequest.Headers[HttpRequestHeader.Authorization]; 
     // if supplied hash is valid, user is authenticated 
     if (IsValidUserKey(authHeader, requestUri)) 
      return true; 
     return false; 
    } 
    public static bool IsValidUserKey(string key, string uri) 
    { 
     string[] authParts = key.Split(':'); 
     if (authParts.Length == 2) 
     { 
      string userid = authParts[0]; 
      string hash = authParts[1]; 
      if (ValidateHash(userid, uri, hash)) 
       return true; 
     } 
     return false; 
    } 

    private static bool ValidateHash(string userid, string uri, string hash) 
    { 
     if (!UserKeys.Contains(userid)) 
      return false; 
     string userkey = userid; 
     byte[] secretBytes = ASCIIEncoding.ASCII.GetBytes(userkey); 
     HMACMD5 hmac = new HMACMD5(secretBytes); 
     byte[] dataBytes = ASCIIEncoding.ASCII.GetBytes(uri); 
     byte[] computedHash = hmac.ComputeHash(dataBytes); 
     string computedHashString = Convert.ToBase64String(computedHash); 
     return computedHashString.Equals(hash); 
    } 
} 

回答

2

取決於所需的安全級別。從jQuery調用確實會打開你的代碼spection,並且可能不安全。一種選擇是使用從jQuery調用的代理頁面,然後代理頁面與web服務進行通信。此時,代理頁面可以設置爲基於主叫IP,主機,隱藏表單字段值等限制訪問。

另一個建議是Base64對所有請求參數進行編碼。不是超級安全的,但需要更多的工作來撬動解碼。

如果您的表單是安全的(例如需要登錄才能訪問),請將當前用戶標識傳遞給webservice,然後在執行任何其他處理之前執行查找以確定該用戶是否存在於webservice中是最理想的。

Web服務正在向公衆開放。如果返回的信息是安全的,則使用憑據進行訪問是必須的。但是如果你沒有任何限制(例如登錄訪問調用頁面),那麼從jQuery做起,它總是很脆弱。

這些只是選項。希望能幫助到你。

=================所附實施例:

代理頁(的.aspx)

protected void Page_Load(object sender, EventArgs e) { 
     string result = ""; 


     if (Request.QueryString["srvcmethod"] != null && Request.QueryString["encodedJSONdata"] != null) { 

      string decodedJSONRequest = encoder.StringFromBase64(Request.QueryString["encodedJSONdata"].ToString().Trim()); 
      string wsdlData = GetWebservice("ccAddressAPI", Request.QueryString["srvcmethod"].ToString().Trim(), decodedJSONRequest); 

      /* 
       * Existance of QueryString('callback') indicates a request initiated from JQuery AJAX on remote server 
       * response must be formatted as JSONP to prevent parse failure (eg 'callback(JSON);') 
      */ 
      if (Request.QueryString["callback"] != null) { 
       string jsonpCallBack = Request.QueryString["callback"]; 
       result = jsonpCallBack + "(" + wsdlData + ");"; 
      } else { 
       result = wsdlData; 
      } 

      Response.Write(result); 
      Response.End(); 
     } 
    } 

    /// <summary> 
    /// Performs post to WebService 
    /// </summary> 
    /// <param name="srvc">Service File Name</param> 
    /// <param name="srvcMethod">Method within service to call</param> 
    /// <param name="jsonData">JSON Serialized form data</param> 
    /// <returns>JSON Serialized string of webservice response</returns> 
    private string GetWebservice(string srvc, string srvcMethod, string jsonData) { 
     string result = null; 
     string wsdlData = null; 
     Dictionary<string, object> obj = new Dictionary<string, object>(); 

     //--define webservice url 
     string currentReq = Request.Url.GetLeftPart(UriPartial.Authority); 
     string servicepath = "/websrvc/"; 
     string wsdl = currentReq + servicepath + srvc + ".svc/" + srvcMethod; 

     //--initiate webservice request 
     try { 
      byte[] postData = Encoding.UTF8.GetBytes(jsonData); 
      WebRequest request = WebRequest.Create(wsdl); 
      request.Method = WebRequestMethods.Http.Post; 
      request.ContentLength = postData.Length; 
      request.ContentType = "text/json"; 
      Stream dataStream = request.GetRequestStream(); 
      dataStream.Write(postData, 0, postData.Length); 
      dataStream.Close(); 

      //--retrieve/store request response 
      WebResponse response = request.GetResponse(); 
      StreamReader reader = new StreamReader(response.GetResponseStream()); 
      wsdlData = reader.ReadToEnd(); 
      reader.Close(); 
      response.Close(); 

     } catch (Exception ex) { 
      logErrors(ex, System.Reflection.MethodBase.GetCurrentMethod().Name); 
     } 

     return wsdlData; 
    } 

jQuery的例子來呼叫代理頁

var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/=",encode:function(input){var output="";var chr1,chr2,chr3,enc1,enc2,enc3,enc4;var i=0;input=Base64._utf8_encode(input);while(i<input.length){chr1=input.charCodeAt(i++);chr2=input.charCodeAt(i++);chr3=input.charCodeAt(i++);enc1=chr1>>2;enc2=((chr1&3)<<4)|(chr2>>4);enc3=((chr2&15)<<2)|(chr3>>6);enc4=chr3&63;if(isNaN(chr2)){enc3=enc4=64}else if(isNaN(chr3)){enc4=64}output=output+this._keyStr.charAt(enc1)+this._keyStr.charAt(enc2)+this._keyStr.charAt(enc3)+this._keyStr.charAt(enc4)}return output},decode:function(input){var output="";var chr1,chr2,chr3;var enc1,enc2,enc3,enc4;var i=0;input=input.replace(/[^A-Za-z0-9\+\/\=]/g,"");while(i<input.length){enc1=this._keyStr.indexOf(input.charAt(i++));enc2=this._keyStr.indexOf(input.charAt(i++));enc3=this._keyStr.indexOf(input.charAt(i++));enc4=this._keyStr.indexOf(input.charAt(i++));chr1=(enc1<<2)|(enc2>>4);chr2=((enc2&15)<<4)|(enc3>>2);chr3=((enc3&3)<<6)|enc4;output=output+String.fro mCharCode(chr1);if(enc3!=64){output=output+String.fromCharCode(chr2)}if(enc4!=64){output=output+String.fromCharCode(chr3)}}output=Base64._utf8_decode(output);return output},_utf8_encode:function(string){string=string.replace(/\r\n/g,"\n");var utftext="";for(var n=0;n<string.length;n++){var c=string.charCodeAt(n);if(c<128){utftext+=String.fromCharCode(c)}else if((c>127)&&(c<2048)){utftext+=String.fromCharCode((c>>6)|192);utftext+=String.fromCharCode((c&63)|128)}else{utftext+=String.fromCharCode((c>>12)|224);utftext+=String.fromCharCode(((c>>6)&63)|128);utftext+=String.fromCharCode((c&63)|128)}}return utftext},_utf8_decode:function(utftext){var string="";var i=0;var c=c1=c2=0;while(i<utftext.length){c=utftext.charCodeAt(i);if(c<128){string+=String.fromCharCode(c);i++}else if((c>191)&&(c<224)){c2=utftext.charCodeAt(i+1);string+=String.fromCharCode(((c&31)<<6)|(c2&63));i+=2}else{c2=utftext.charCodeAt(i+1);c3=utftext.charCodeAt(i+2);string+=String.fromCharCode(((c&15)<<12)|((c2&63)<<6)|(c3&63));i+=3}}return strin g}} 

    var data = JSON.stringify({ 
      "EmployeeId": EmployeeId, 
      "LoginAccountId": LoginAccountId, 
      "CustomerId": CustomerId, 
      "CostCentreId": CostCentreId, 
      "ParentCostCentreId": ParentCostCentreId, 
      "CostCentreAdministratorId": CostCentreAdministratorId, 
      "UserType": UserType, 
      "optionValList": optionValList 
     }); 

     var jsondata = Base64.encode(data); 
     var srvcmethod = 'initCostCentreAddressDataProvider'; 
     $.ajax({ 
      url: "[proxypageurl.aspx]", 
      type: "GET", 
      cache: false, 
      contentType: "application/json; charset=utf-8", 
      dataType: "jsonp", 
      data: { "srvcmethod": srvcmethod, "encodedJSONdata": jsondata }, 
      error: function (XMLHttpRequest, textStatus, errorThrown) { console.log({ 'XMLHttpRequest': XMLHttpRequest, 'textStatus': textStatus, 'errorThrown': errorThrown }); }, 
      success: function (jsonRes) { 

       var json = JSON.parse(jsonRes); 
     //additional processing here    
      } 

     }); 

我不想回答有問題的問題,但您有什麼安全需求,以及服務響應的返回類型是什麼?

從後面的代碼調用Web方法是更自我包含。唯一顯着的區別是web方法通常只能從調用頁面或應用程序訪問,除非您將響應格式化爲JSONP,否則無法從其他域中獲得。 Web服務是公開可用的,默認情況下通常會返回XML,但是如果您從Web服務返回JSON,則其他域也不可用,僅適用於您。

所以也許從web服務格式化您的響應爲JSON?在那一點上,即使我知道你的服務,我也無法調用它,除非你格式化爲JSONP。

上面的示例使用JSONP作爲jQuery,因爲我的webservice位於調用頁面的單獨域中。這可以修改爲僅返回JSON,並且服務器端邏輯應該仍然可以工作(但是在該用例中它是未測試的)。

希望有所幫助。

+0

謝謝bphillips的幫助。我已經編輯我的問題來添加代碼示例。你說「將當前用戶ID傳遞給web服務」,但是如何避免在html/js源代碼中顯示userId?在我的示例中,您可以輕鬆找到secretkey「userid:uCMfSzkjue + HSDygYB5aEg ==」,並呼叫我休息WS :( – toregua

+0

您認爲最佳做法是避免從jquery調用REST WS嗎?是更好地調用webmethod還是舊的代碼asmx WS?我完全失去了所有的解決方案... – toregua

+0

你有一個代理頁面的例子嗎? – toregua

1

所以我編碼我自己的代理,它似乎工作:

[System.Web.Services.WebMethod()] 
[System.Web.Script.Services.ScriptMethod(ResponseFormat = ResponseFormat.Json)] 
public static object Call(string WSUrl) 
{ 
    // Create web client. 
    WebClient client = new WebClient(); 
    client.Encoding = Encoding.UTF8; 

    // Download string. 
    JavaScriptSerializer serializer = new JavaScriptSerializer(); 
    return serializer.Deserialize<List<object>>(client.DownloadString(WSUrl)); 
} 

,它可以被稱爲像使用jQuery:

$.ajax({ 
    type: "POST", 
    url: "http://localhost:14892/common/proxy.aspx/call", 
    data: "{'WSUrl':'" + rootWS + "ListeService/2/GetListeSerie" + "(" + listeId + ")" + "'}", 
    contentType: "application/json; charset=utf-8", 
    dataType: "json", 
    success: function (response) { 
     var result = response.d; 
     var new_record = ""; 
     $.each(result, function (index, res) { 
      new_record = serieListe(index + 1, res.Id, res.ImageUrl, res.SerieNom, res.SerieSynopsis, res.SerieUrl); 
      $('#gridElementsListe').append(new_record); 
     }); 
    }, 
    error: function (msg) { 
     $('#msg').html("Error while calling web service,,") 
    } 
}); 

希望其幫助。現在我將爲我的代理添加一些安全性,正如bphillips向我暗示的那樣。如果你有任何建議有一個很好的安全協議不要猶豫,在這裏分享;)

+1

祝你好運!這些可能會很棘手。請注意,如果您已經創建了webservice,則不需要報廢。我附加了一個調用您的服務的代理頁面的示例。把它想象成你的javascript和你的webservice之間的橋樑。你實際上可以添加一個變量到代理頁面,如'string accesskey =「mysecretkey」;'然後在web服務中添加該參數作爲必需的參數,如果它不存在,則返回錯誤。這樣,您可以通過代理訪問該服務的唯一方式。哈克,但在捏工作。 – bphillips

+0

事實上,我需要開發其餘的Web服務,以便從我的jquery ajax接口調用它們,並通過其他Web客戶端(如移動應用程序(android))調用它們。爲了做到這一點,我遵循了你的建議,並且我創建了一個代理頁面(爲了避免在javascript中顯示標題,它在後面的代碼中生成)。爲了安全起見,我添加了像amazon的時間戳檢查和一個使用HMAC編碼的SALT的secretkey。爲了保護HTTP標題,我想我會在生產環境中使用SSL。你對這個解決方案有什麼看法? – toregua

+0

而我所有的web服務使用:[WebInvoke(Method =「GET」,ResponseFormat = WebMessageFormat.Json,UriTemplate =「Method({id})」)] so JSON。這就是爲什麼我在我的代理中使用JavaScriptSerializer來返回相同類型 – toregua

相關問題