Cookies, Sessions and
User Authentication

     Thierry Sans
Today, we will



•   bake cookies

•   and use cookies to implement sessions

•   and use sessions to authenticate users

•   and define user’s permissions
Before we start
Security assumptions



Client Side                         Server Side




Web Browser            Web Server       Database
Security assumptions
                       You have absolutely no control
                       on the client


Client Side                                   Server Side




Web Browser                      Web Server       Database
Cookies
The big picture


                   key/value pairs data
Client Side                                         Server Side
                                     HTTP request

                          HTTP response
                                     HTTP request

                          HTTP response



Web Browser                                           Web Server
Cookies




•   Cookies are pieces of data sent back and forth between
    the browser and the server in HTTP request and response
Anatomy of a Cookie




•   Text data (Up to 4kb)

•   May (or may not) have an expiration date

•   Can be manipulated from the client and the server
What cookies are useful for?



•   Shopping cart

•   Browsing preferences

•   “Remember me on this computer”

•   User authentication
Manipulating cookies




•   A cookie can be modified

    •   on the server side - Django

    •   on the client side - jQuery Cookie plugin
Remember the search input (in Javascript)

                                             WebDirectory/static/js/init.js
   function search(){
       var input = $.trim($("input[name='search']").val());
       $.cookie('keywords', input);


                              storing data
                                             WebDirectory/static/js/init.js
   function init(){
     if ($.cookie("keywords")){
       $("input[name='search']").val($.cookie("keywords"));
       search();
     }
   }
                                        retrieving data
Remember the number of visits (in Django)

                                               WebDirectory/views.py
 def index(request):
   entry_list = Entry.objects.all()      retrieving data
   if 'nb_visits' in request.COOKIES:
       n = int(request.COOKIES['nb_visits']) + 1
   else:
       n = 1
   response = render_to_response('WebDirectory/index.html',
                   {'entry_list': entry_list, 'nb_visits': n})
   response.set_cookie('nb_visits', value=n,
                        max_age=None, expires=None,
                        path='/webdirectory/', domain=None,
                        secure=None, httponly=False)
   return response

                                      storing data
Firefox - debugging (and hacking) cookies
Hacking cookies




The user can create, modify, delete key/value pairs in cookies
Sessions
The big picture


                   session id
Client Side                                   Server Side
                              HTTP request

                   HTTP response
                              HTTP request

                   HTTP response



Web Browser                                     Web Server

                       key/value pairs data
The concept of session



•   There is a session id (aka token)
    between the browser and the web application

•   This session id should be unique and unforgeable
    (usually a long random number or a hash)

•   This session id is bind to key/value pairs data
Where sessions values are stored




•   Session ID is stored in a cookie

•   Session key/value pairs are stored on the server


                                       in the database
                                       with Django
Remember the number of visits using sessions


                                               WebDirectory/views.py
 def index(request):
   if 'nb_visits' in request.session:
           n = int(request.session['nb_visits']) + 1
   else:
           n = 1                           retrieving data
   request.session['nb_visits'] = n
   response = render_to_response('WebDirectory/index.html',
                   {'entry_list': entry_list, 'nb_visits': n})
   return response


             storing data
Hacking sessions




The user can create, modify, delete the session ID in the cookie

But cannot access the key/value pairs stored on the server
Clearing the session

              delete the cookie
 Dirty
              (but the session values are still on the server)


              use flush() in the view to delete the current
 Program
              session data and regenerate the session key

         django-admin.py cleanup
 Command deletes any session in the session table whose
         expire_date is in the past
User Authentication
The simple recipe for user authentication


1. Ask the user for a login and password and send it
   to the server (HTTP/POST request)

2. Verify the login/password based on information
   stored on the server (usually in the database)

3. Start a session if the login password matches i.e. once
   the user has been successfully authenticated

4. Grant access to resources according to the session
Django login/logout urls


               Django predefined login view
                                                    WebDirectory/urls.py
urlpatterns += patterns('',
    (r'^login/$',    'django.contrib.auth.views.login',
                    {'template_name': 'WebDirectory/login.html'}),
    (r'^logout/$', 'WebDirectory.views.logout_view'))


                                          User’s defined login page

     User defined logout view
Or your can manage your own login view
                                                          example
from django.contrib.auth import authenticate, login


def login_view(request):
   username = request.POST['username']
   password = request.POST['password']
   user = authenticate(username=username, password=password)
    if user is not None:
        if user.is_active:
            login(request, user)
            # Redirect to a success page.
        else:
            # Return a 'disabled account' error message
    else:
       # Return an 'invalid login' error message.
Logout


                                                     WebDirectory/views.py
from django.contrib.auth import logout


def logout_view(request):
   logout(request)
    return HttpResponseRedirect(reverse('WebDirectory.views.index',))
Protecting resources




•   Certain views might be accessible by the authenticated users
    only
Version 1 - using the template




{% if user.is_authenticated %}
    <p>Welcome, {{ user.username }}. Thanks for logging in.</p>
{% else %}
    <p>Welcome, new user. Please log in.</p>
{% endif %}
Version 2 - using the views




def index(request):
    if request.user.is_authenticated():
       # Do something for authenticated users.
    else:
       # Do something for anonymous users.
Version 3 - using a decorator in the view




from django.contrib.auth.decorators import login_required


@login_required(login_url='/myapp/login/')
def index(request):
   # Do something for authenticated users.
WebDirectory - security policy




1. Only authenticated users can see the web gallery
WebDirectory


                                                    WebDirectory/views.py
@login_required(login_url='/webdirectory/login/')
def index(request):
  entry_list = Entry.objects.all()
  ...
WebDirectory


                                                    WebDirectory/views.py
@login_required(login_url='/webdirectory/login/')
def index(request):
  entry_list = Entry.objects.all()
  ...



               But, we must also protect all the other views:
               • getImage
               • add
               • search
Authorization
WebDirectory - security policy




1. Only authenticated users can see the web gallery

2. Only the admin user “tsans” can add a new entry
Version 0 - hide the upload button (template)

                                 WebDirectory/templates/WebDirectoryindex.html

{% if admin %}
   <a href="#" id="publisherButton"
                 onclick="showHideUploader();return false;">
   Show uploader
   </a>
   <div id="publisher">
          ...
          ...
   </div>
{% endif %}
Version 0 -hide the upload button (view)


                                                WebDirectory/views.py
@login_required(login_url='/webdirectory/login/')
def index(request):
  entry_list = Entry.objects.all()
  ...
  return render_to_response('WebDirectory/index.html',
                            {'entry_list': entry_list,
                              request.user.username=='tsans'})
Version 0 -hide the upload button (view)


                                                 WebDirectory/views.py
@login_required(login_url='/webdirectory/login/')
def index(request):
  entry_list = Entry.objects.all()
  ...
  return render_to_response('WebDirectory/index.html',
                             {'entry_list': entry_list,
                               request.user.username=='tsans'})



                      This is absolutely not secure enough !!!
Version 1 - protecting the view


                                                WebDirectory/views.py

@login_required(login_url='/webdirectory/login/')
def add(request):
 if (request.user.username == 'tsans')
        # add the entry to the database
    else:
        raise Http500
Django permissions



•   Based on the Django admin features, the model Entity
    predefines 3 permissions:

    •   Entry.add_entry

    •   Entry.change_entry

    •   Entry.delete_entry
Version 2 - using permissions in the view


                                                    WebDirectory/views.py

@login_required(login_url='/webdirectory/login/')
def add(request):
 if request.user.has_perm('Entry.add_entity'):
        # add the entry to the database
    else:
        raise Http500
Version 3 - - using a decorator in the view




                                          WebDirectory/views.py

@permission_required('Entry.add_entry')
def add(request):
  # add the entry to the database
Define custom permissions


                                                            example

class Task(models.Model):
 ...
  class Meta:
    permissions = (
        ("view_task", "Can see available tasks"),
        ("change_task_status", "Can change the status of tasks"),
        ("close_task", "Can close a task"),
    )
Summary




•   What is the difference between a cookie and a session?

•   How are users authenticated?

•   What is the difference between authentication and
    authorization?

Authentication

  • 1.
    Cookies, Sessions and UserAuthentication Thierry Sans
  • 2.
    Today, we will • bake cookies • and use cookies to implement sessions • and use sessions to authenticate users • and define user’s permissions
  • 3.
  • 4.
    Security assumptions Client Side Server Side Web Browser Web Server Database
  • 5.
    Security assumptions You have absolutely no control on the client Client Side Server Side Web Browser Web Server Database
  • 6.
  • 7.
    The big picture key/value pairs data Client Side Server Side HTTP request HTTP response HTTP request HTTP response Web Browser Web Server
  • 8.
    Cookies • Cookies are pieces of data sent back and forth between the browser and the server in HTTP request and response
  • 9.
    Anatomy of aCookie • Text data (Up to 4kb) • May (or may not) have an expiration date • Can be manipulated from the client and the server
  • 10.
    What cookies areuseful for? • Shopping cart • Browsing preferences • “Remember me on this computer” • User authentication
  • 11.
    Manipulating cookies • A cookie can be modified • on the server side - Django • on the client side - jQuery Cookie plugin
  • 12.
    Remember the searchinput (in Javascript) WebDirectory/static/js/init.js function search(){ var input = $.trim($("input[name='search']").val()); $.cookie('keywords', input); storing data WebDirectory/static/js/init.js function init(){ if ($.cookie("keywords")){ $("input[name='search']").val($.cookie("keywords")); search(); } } retrieving data
  • 13.
    Remember the numberof visits (in Django) WebDirectory/views.py def index(request): entry_list = Entry.objects.all() retrieving data if 'nb_visits' in request.COOKIES: n = int(request.COOKIES['nb_visits']) + 1 else: n = 1 response = render_to_response('WebDirectory/index.html', {'entry_list': entry_list, 'nb_visits': n}) response.set_cookie('nb_visits', value=n, max_age=None, expires=None, path='/webdirectory/', domain=None, secure=None, httponly=False) return response storing data
  • 14.
    Firefox - debugging(and hacking) cookies
  • 15.
    Hacking cookies The usercan create, modify, delete key/value pairs in cookies
  • 16.
  • 17.
    The big picture session id Client Side Server Side HTTP request HTTP response HTTP request HTTP response Web Browser Web Server key/value pairs data
  • 18.
    The concept ofsession • There is a session id (aka token) between the browser and the web application • This session id should be unique and unforgeable (usually a long random number or a hash) • This session id is bind to key/value pairs data
  • 19.
    Where sessions valuesare stored • Session ID is stored in a cookie • Session key/value pairs are stored on the server in the database with Django
  • 20.
    Remember the numberof visits using sessions WebDirectory/views.py def index(request): if 'nb_visits' in request.session: n = int(request.session['nb_visits']) + 1 else: n = 1 retrieving data request.session['nb_visits'] = n response = render_to_response('WebDirectory/index.html', {'entry_list': entry_list, 'nb_visits': n}) return response storing data
  • 21.
    Hacking sessions The usercan create, modify, delete the session ID in the cookie But cannot access the key/value pairs stored on the server
  • 22.
    Clearing the session delete the cookie Dirty (but the session values are still on the server) use flush() in the view to delete the current Program session data and regenerate the session key django-admin.py cleanup Command deletes any session in the session table whose expire_date is in the past
  • 23.
  • 24.
    The simple recipefor user authentication 1. Ask the user for a login and password and send it to the server (HTTP/POST request) 2. Verify the login/password based on information stored on the server (usually in the database) 3. Start a session if the login password matches i.e. once the user has been successfully authenticated 4. Grant access to resources according to the session
  • 25.
    Django login/logout urls Django predefined login view WebDirectory/urls.py urlpatterns += patterns('', (r'^login/$', 'django.contrib.auth.views.login', {'template_name': 'WebDirectory/login.html'}), (r'^logout/$', 'WebDirectory.views.logout_view')) User’s defined login page User defined logout view
  • 26.
    Or your canmanage your own login view example from django.contrib.auth import authenticate, login def login_view(request): username = request.POST['username'] password = request.POST['password'] user = authenticate(username=username, password=password) if user is not None: if user.is_active: login(request, user) # Redirect to a success page. else: # Return a 'disabled account' error message else: # Return an 'invalid login' error message.
  • 27.
    Logout WebDirectory/views.py from django.contrib.auth import logout def logout_view(request): logout(request) return HttpResponseRedirect(reverse('WebDirectory.views.index',))
  • 28.
    Protecting resources • Certain views might be accessible by the authenticated users only
  • 29.
    Version 1 -using the template {% if user.is_authenticated %} <p>Welcome, {{ user.username }}. Thanks for logging in.</p> {% else %} <p>Welcome, new user. Please log in.</p> {% endif %}
  • 30.
    Version 2 -using the views def index(request): if request.user.is_authenticated(): # Do something for authenticated users. else: # Do something for anonymous users.
  • 31.
    Version 3 -using a decorator in the view from django.contrib.auth.decorators import login_required @login_required(login_url='/myapp/login/') def index(request): # Do something for authenticated users.
  • 32.
    WebDirectory - securitypolicy 1. Only authenticated users can see the web gallery
  • 33.
    WebDirectory WebDirectory/views.py @login_required(login_url='/webdirectory/login/') def index(request): entry_list = Entry.objects.all() ...
  • 34.
    WebDirectory WebDirectory/views.py @login_required(login_url='/webdirectory/login/') def index(request): entry_list = Entry.objects.all() ... But, we must also protect all the other views: • getImage • add • search
  • 35.
  • 36.
    WebDirectory - securitypolicy 1. Only authenticated users can see the web gallery 2. Only the admin user “tsans” can add a new entry
  • 37.
    Version 0 -hide the upload button (template) WebDirectory/templates/WebDirectoryindex.html {% if admin %} <a href="#" id="publisherButton" onclick="showHideUploader();return false;"> Show uploader </a> <div id="publisher"> ... ... </div> {% endif %}
  • 38.
    Version 0 -hidethe upload button (view) WebDirectory/views.py @login_required(login_url='/webdirectory/login/') def index(request): entry_list = Entry.objects.all() ... return render_to_response('WebDirectory/index.html', {'entry_list': entry_list, request.user.username=='tsans'})
  • 39.
    Version 0 -hidethe upload button (view) WebDirectory/views.py @login_required(login_url='/webdirectory/login/') def index(request): entry_list = Entry.objects.all() ... return render_to_response('WebDirectory/index.html', {'entry_list': entry_list, request.user.username=='tsans'}) This is absolutely not secure enough !!!
  • 40.
    Version 1 -protecting the view WebDirectory/views.py @login_required(login_url='/webdirectory/login/') def add(request): if (request.user.username == 'tsans') # add the entry to the database else: raise Http500
  • 41.
    Django permissions • Based on the Django admin features, the model Entity predefines 3 permissions: • Entry.add_entry • Entry.change_entry • Entry.delete_entry
  • 42.
    Version 2 -using permissions in the view WebDirectory/views.py @login_required(login_url='/webdirectory/login/') def add(request): if request.user.has_perm('Entry.add_entity'): # add the entry to the database else: raise Http500
  • 43.
    Version 3 -- using a decorator in the view WebDirectory/views.py @permission_required('Entry.add_entry') def add(request): # add the entry to the database
  • 44.
    Define custom permissions example class Task(models.Model): ... class Meta: permissions = ( ("view_task", "Can see available tasks"), ("change_task_status", "Can change the status of tasks"), ("close_task", "Can close a task"), )
  • 45.
    Summary • What is the difference between a cookie and a session? • How are users authenticated? • What is the difference between authentication and authorization?