Bir önceki yazımızda bahsettiğimiz Pagemethod’ların birçok dezavantajına karşılık web servisleri her zaman paramatrik yapısı ve birçok özelliği ile tercih edilebilir durumdadır. Fakat bu noktada güvenlik gibi bir unsura değinmeden edemeyeceğiz çünkü yazımızın konusu 

Web servislerinde güvenliği sağlamak için aşağıdaki güvenlik önlemlerini geliştireceğimiz sisteme göre tercih edebiliriz.
1- Adding Service Token To Header
2- Session Control
3- Ssl certification
4- Form Authentication
Yukarıdaki güvenlik önlemlerinden önce web.config içerisinden projelerimizde kullandığımız webservisleri için genel ayaları yönetebiliriz. Aşağıdaki yorum satırı haline getirilen satırlarda ilgili değişiklikleri yaparak servislerin davranışlarını projemize göre özelleştirebiliriz.
- <system.web.extensions>
- <scripting>
- <webservices>
- <!-- Uncomment this line to customize maxJsonLength and add a custom converter -->
- <!--
- <jsonSerialization maxJsonLength="500">
- <converters>
- <add name="ConvertMe" type="Acme.SubAcme.ConvertMeTypeConverter"/>
- </converters>
- </jsonSerialization>
- -->
- <!-- Uncomment this line to enable the authentication service. Include requireSSL="true" if appropriate. -->
- <!--
- <authenticationService enabled="true" requireSSL = "true|false"/>
- -->
- <!-- Uncomment these lines to enable the profile service. To allow profile properties to be retrieved
- and modified in ASP.NET AJAX applications, you need to add each property name to the readAccessProperties and
- writeAccessProperties attributes. -->
- <!--
- <profileService enabled="true"
- readAccessProperties="propertyname1,propertyname2"
- writeAccessProperties="propertyname1,propertyname2" />
- -->
- </webservices>
- <scriptresourcehandler enablecompression="true" enablecaching="true">
- </scriptresourcehandler></scripting>
- </system.web.extensions>
Adding Service Token To Header
Servislerimizi referans olarak çağırdığımız sayfarımızda dinamik olarak Service Token üreterek bu token’ı webrequestlerimizin header collection’ına eklememiz gerekmektedir. Servisimizi çağırdığımız sayfamızın aspx ve cs bölümlerini aşağıdaki oluşturuyoruz. Cs tarafında SERVICE_TOKEN Nesnemizi oluşturup sayfamıza ekliyoruz. Bu Token’ı Cache nesnesine ekleyerek servis tarafında bu nesnenin değerini kontrol ediyoruz. Ayrıca nesneyi request’lerimize header olarak eklememiz gerekiyor. Böylece web servisinin çağrıldığı sayfa ile asenkron olmasını sağlıyoruz. Herhangi bir adresten yapılan requestler dikkate alınmayacaktır. Servis tarafında üretilen exception’ı göreceklerdir.
Default.aspx
- <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebServiceSecurity._Default" %>
- <script type="text/javascript">
- function pageLoad(sender, args) {
- Sys.Net.WebRequestManager.add_invokingRequest(onInvoke);
- }
- function pageUnload(sender, args) {
- Sys.Net.WebRequestManager.remove_invokingRequest(onInvoke);
- }
- function onInvoke(sender, args) {
- args.get_webRequest().get_headers()['serviceToken'] = SERVICE_TOKEN;
- }
- function GetMessageSecurely() {
- WebServiceSecurity.MessageService.SecureMessage(OnSuccess, OnFail);
- }
- function OnSuccess(result) {
- alert(result);
- }
- function OnFail(error) {
- error.get_message();
- }
- </script>
- <form id="form1" runat="server">
- <asp:scriptmanager id="ScriptManager1" ene="" runat="server">
- <services>
- <asp:servicereference path="~/MessageService.asmx">
- </asp:servicereference></services>
- </asp:scriptmanager>
- <div>
- <asp:button id="btnTestService" runat="server" text="Test Service" onclientclick="javascript:GetMessageSecurely();">
- </asp:button></div>
- </form>
Default.aspx.cs
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.UI;
- using System.Web.UI.WebControls;
- namespace WebServiceSecurity
- {
- public partial class _Default : System.Web.UI.Page
- {
- protected void Page_Load(object sender, EventArgs e)
- {
- GenerateSecurityTicket();
- }
- private void GenerateSecurityTicket()
- {
- string cacheKey = User.Identity.Name + ":serviceToken";
- string securityTicket = Guid.NewGuid().ToString();
- Cache[cacheKey] = securityTicket;
- string script = string.Format("SERVICE_TOKEN = '{0}';", securityTicket);
- ScriptManager.RegisterClientScriptBlock(this, this.GetType(), "serviceToken", script, true);
- }
- }
- }
MessageService.asmx.cs
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.Services;
- using System.Security;
- using System.Web.Script.Services;
- namespace WebServiceSecurity
- {
- ///
- <summary>
- /// Summary description for MessageService
- /// </summary>
- [WebService(Namespace = "http://tempuri.org/")]
- [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
- [System.ComponentModel.ToolboxItem(false)]
- //To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
- [ScriptService]
- public class MessageService : System.Web.Services.WebService
- {
- [WebMethod]
- public string SecureMessage()
- {
- EnsureTicket();
- return "Valid Request for " + HttpContext.Current.User.Identity.Name;
- }
- private void EnsureTicket()
- {
- HttpContext context = HttpContext.Current;
- string secid = context.Request.Headers["serviceToken"];
- if (string.IsNullOrEmpty(secid))
- {
- throw new SecurityException("Can Not Find Service Token");
- }
- string cacheKey = context.User.Identity.Name + ":serviceToken";
- string cacheTicket = (string)context.Cache[cacheKey];
- if (string.Compare(secid, cacheTicket, false) != 0)
- {
- throw new SecurityException("Wrong Service Token");
- }
- }
- }
- }
Geçersiz sayfalardan requestların sonucunu test etmek için aşağıdaki sayfayı ekledim.
TestRequest.aspx
- <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="TestRequest.aspx.cs" Inherits="WebServiceSecurity.TestRequest" %>
- <script type="text/javascript">
- function pageLoad(sender, args) {
- Sys.Net.WebRequestManager.add_invokingRequest(onInvoke);
- }
- function pageUnload(sender, args) {
- Sys.Net.WebRequestManager.remove_invokingRequest(onInvoke);
- }
- function onInvoke(sender, args) {
- args.get_webRequest().get_headers()['serviceToken'] = "wrong token"; //SERVICE_TOKEN;
- }
- function GetMessageSecurely() {
- WebServiceSecurity.MessageService.SecureMessage(OnSuccess, OnFail);
- }
- function OnSuccess(result) {
- alert(result);
- }
- function OnFail(error) {
- alert(error.get_message());
- }
- </script>
- <form id="form1" runat="server">
- <asp:scriptmanager id="ScriptManager1" runat="server">
- <services>
- <asp:servicereference path="~/MessageService.asmx">
- </asp:servicereference></services>
- </asp:scriptmanager>
- <div>
- <input type="button" title="TEST SERVICE" value="TEST SERVICE" onclick="javascript:GetMessageSecurely();">
- </div>
- </form>
Not: Yukarıdaki javascript fonksiyonlarında tetiklenen eventler update panel kullanılan sayfalarda da tetiklenebilir.
Yukarıda sayfaya eklenen hatalı serviceToken nesnesi servise gönderildiğinde servis tarafında hatalı sonuç elde edilecektir. Default.aspx.cs sayfasındaki Header nesnesini Cashe kaydeden kod parçağı ve aspx tarafında header koleksiyonuna ekleyen scriptler kaldırıldığında da “servis tarafından yetkili olmayan kullanıcı” şeklinde cevap dönecektir.
2. Session Control
Yukarıda yaptığımız örnek uygulamanı aynısını Session Control içinde uygulayabiliriz. Servisin çağırılacağı sayfayı aşağıdaki gibi oluşturabiliriz. Bu sayfada üretilen ServiceToken session nesnesinin servis tarafında olup olmadığı kontrol edilerek servisin güvenlik kontrolü yapılmış olur.
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.UI;
- using System.Web.UI.WebControls;
- namespace WebServiceSecurity
- {
- public partial class SessionControl : System.Web.UI.Page
- {
- protected void Page_Load(object sender, EventArgs e)
- {
- if (Session["ServiceToken"] == null) Session["ServiceToken"] = Guid.NewGuid().ToString() + ":ServiceToken";
- }
- }
- }
Dikkat etmemiz gereken nokta webservis içerisinde kullandığımız
GetMessage()
metoduna çalışma anında bir özellik değeri(attribute) atamalıyız. [WebMethod(EnableSession=true)]
Enable Session = true WebMothod attribute’üne parametre belirtmeliyiz. Böylece web sayfalarımızda ürettiğimiz session nesnelerine HttpContext.Current.Session ile ulaşabiliriz.
Not: Projemizdeki tüm sayfalardan yapılacak requestlerin kontrolünü sağlamak istiyorsak gloabal.asax dosyamızda Session Start Event’ında bu değeri otomatik olarak oluşturabiliriz.
- protected void Session_Start(object sender, EventArgs e)
- {
- Session["ServiceToken"] = Guid.NewGuid().ToString() + ":ServiceToken";
- }
WebServiceActiveSession.asmx .cs
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.Services;
- namespace WebServiceSecurity
- {
- ///
- <summary>
- /// Summary description for WebServiceActiveSession
- /// </summary>
- [WebService(Namespace = "http://tempuri.org/")]
- [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
- [System.ComponentModel.ToolboxItem(false)]
- // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
- [System.Web.Script.Services.ScriptService]
- public class WebServiceActiveSession : System.Web.Services.WebService
- {
- [WebMethod(EnableSession=true)]
- public string GetMessage()
- {
- string result="";
- if (HttpContext.Current.Session["ServiceToken"] != null)
- result = "Authenticated User For Web Service " + Session["ServiceToken"].ToString();
- else
- result ="Acces Denied to This Service";
- return result;
- }
- }
- }
Yukarıda servisi test edebilmemiz için aşağıdaki gibi bir sayfa hazırlamak yeterli olacaktır.
SessionControl.aspx
- <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="SessionControl.aspx.cs" Inherits="WebServiceSecurity.SessionControl" %>
- <script type="text/javascript">
- function GetMessageSecurely() {
- WebServiceSecurity.WebServiceActiveSession.GetMessage(OnSuccess, OnFail);
- }
- function OnSuccess(result) {
- alert(result);
- }
- function OnFail(error) {
- alert(error.get_message());
- }
- </script>
- <form id="form1" runat="server">
- <asp:scriptmanager id="ScriptManager1" runat="server">
- <services>
- <asp:servicereference path="~/WebServiceActiveSession.asmx">
- </asp:servicereference></services>
- </asp:scriptmanager>
- <div>
- <input type="button" title="TEST SERVICE" value="TEST SERVICE" onclick="javascript:GetMessageSecurely();">
- </div>
- </form>
SessionControl.aspx.cs
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.UI;
- using System.Web.UI.WebControls;
- namespace WebServiceSecurity
- {
- public partial class SessionControl : System.Web.UI.Page
- {
- protected void Page_Load(object sender, EventArgs e)
- {
- if (Session["ServiceToken"] == null) Session["ServiceToken"] = Guid.NewGuid().ToString() + ":ServiceToken";
- }
- }
- }
Web servisleri yazı dizimizde ilk etapta güvenlik konusunu ele aldık. Güvenlik konusunda diğer 2 maddeye gelecek yazımızda değineceğiz
Hiç yorum yok:
Yorum Gönder