Django decorators

I am developing a page with Django and I had one interesting requirement: limit the number of users. The idea is to let a fixed number of users use the site and show a message telling the others to wait. I will like to share how I did it.

We need to check in every view used by a user. Instead of repeating the same code, the easiest way is to add a decorator for each function. Is really similar to AoP.

decorators.py:

from django.conf import settings
from django.shortcuts import redirect

MAX_NUMBERS_OF_USERS = getattr(settings, 'MAX_NUMBERS_OF_USERS', 100)

def check_user(view_function):
    def _wrapped_view_func(request, *args, **kwargs):
        auth_user = request.user
        
        if auth_user.id <= MAX_NUMBERS_OF_USERS:
            return view_function(request, *args, **kwargs)
        return redirect('coming_soon')
    return _wrapped_view_func

views.py:

from app.decorators import check_user
from django.contrib.auth.decorators import login_required
from django.shortcuts import render

@login_required
@check_user
def restricted_view(request):
    #Do stuff

def coming_soon(request):
    return render(request, 'coming_soon.html', {})

And that’s it ! Easy, clean and simple ! You can do more complex stuffs like logging.

Advertisements

Django + nginx + uWSGI

I had to deploy a web application I just created using Django. I will describe the steps I followed.

  1. Make sure you have you Django setted up correctly. I followed this guide.
  2. Install uWSGI into your virtualenv:
    pip install uwsgi
    
  3. Install nginx:
    sudo aptitude install nginx
    sudo service nginx start
    
  4. Edit nginx.conf file (it is in /etc/nginx if you are using ubuntu), it should be something like this:
    upstream django {
     server unix:///path/to/your/mysite/mysite.sock;
    }
    
    server {
     listen 80 default_server;
     listen [::]:80 default_server ipv6only=on;
    
     server_name mysite.com;
    
     charset utf-8;
    
     # max upload size
     client_max_body_size 5M; # adjust to taste
    
     location /robots.txt {
     alias /path/to/your/mysite/robots.txt;
     }
    
     # Django media
     location /files {
     alias /path/to/your/mysite/files; # your Django project's media files - amend as required
     }
    
     location /static {
     alias /path/to/your/mysite/static; # your Django project's static files - amend as required
     }
    
     # Finally, send all non-media requests to the Django server.
     location / {
     uwsgi_pass django;
     include /path/to/your/mysite/uwsgi_params; # the uwsgi_params file you installed
     }
    }
    

    Make sure it matches your Django app settings.py configuration:

    PROJECT_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
    MEDIA_ROOT = os.path.join(PROJECT_ROOT, 'files')
    MEDIA_URL = '/files/'
    STATIC_ROOT = ''
    STATIC_URL = '/static/'
    
  5. Configure you uWSGI .ini file:
    # mysite_uwsgi.ini file
    [uwsgi]
    
    # Django-related settings
    # the base directory (full path)
    chdir = /path/to/your/mysite
    # Django's wsgi file
    module = mysite.wsgi
    # the virtualenv (full path) .virtualenvs at home folder
    home = /path/to/your/mysitevirtualenv  
    
    # process-related settings
    # master
    master = true
    # maximum number of worker processes
    processes = 10
    # the socket (use the full path to be safe
    socket = /path/to/your/mysite.sock
    # ... with appropriate permissions - may be needed
    chmod-socket = 666
    # clear environment on exit
    vacuum = true
    lgger = syslog
    
  6. Add uwsgi_params file to your project:
    uwsgi_param QUERY_STRING $query_string;
    uwsgi_param REQUEST_METHOD $request_method;
    uwsgi_param CONTENT_TYPE $content_type;
    uwsgi_param CONTENT_LENGTH $content_length;
    
    uwsgi_param REQUEST_URI $request_uri;
    uwsgi_param PATH_INFO $document_uri;
    uwsgi_param DOCUMENT_ROOT $document_root;
    uwsgi_param SERVER_PROTOCOL $server_protocol;
    uwsgi_param HTTPS $https if_not_empty;
    
    uwsgi_param REMOTE_ADDR $remote_addr;
    uwsgi_param REMOTE_PORT $remote_port;
    uwsgi_param SERVER_PORT $server_port;
    uwsgi_param SERVER_NAME $server_name;
    
  7. Run in emperor mode:
    # create a directory for the vassals
    sudo mkdir /etc/uwsgi
    sudo mkdir /etc/uwsgi/vassals
    # symlink from the default config directory to your config file
    sudo ln -s /path/to/your/mysite/mysite_uwsgi.ini /etc/uwsgi/vassals/
    /usr/local/bin/uwsgi --emperor /etc/uwsgi/vassals --uid www-data --gid www-data
    

    You can run the last command on screen or when the system starts.

And thats it, hope this can be helpful. You can find more info here.

XSRF Django

En la web uno se debe cuidar de tres cosas (y muchas otras, pero de las principales y más comunes para mí) son: inyecciones SQL, XSS y XSRF.

La primera como dice el nombre es simplemente a través de llamadas HTTP ingresar código de SQL (esto puede aplicar a otro tipo de bases de datos No-SQL). La forma más común es a la hora de un formulario por el cual se pasan datos bien sea por POST o GET, colocar valores estílo ‘; comando sql;  por medio del cual se pueden robar valores, dañar la base datos, etc.

Luego tenemos los XSS, siglas de cross-site scripting, lo cual es ingresar código en la página que se ataca. Ejemplo un párametro que se pasa por GET y que se imprime en la página. Con poner en ese parámetro algo estilo < iframe src = “http://paginamaliciosa” > </ iframe >  se logra que otras personas vean lo que uno desee y se puede robar información u otras cosas.

Finalmente tenemos el XSRF, siglas de cross-site request forergy. Éste ataque es más complejo, pero puede hacer mucho daño. Consiste en hacer que un usuario autentificado en una página haga cosas que él no autorizo. Un ejemplo sería el siguiente:

  • Un usuario anda autentificado en X página, la cual hace el cambio de contraseña por POST.
  • Una persona maliciosa simplemente debe recrear ese request de POST y hacer que el usuario lo haga sin darse cuenta.

Yo ya en un post pasado publique un link (http://google-gruyere.appspot.com/) que encontré muy bueno que habla de seguridad de páginas web y que explican de estos ataques y como defenderse.

Hable de todo ésto ya que me topé con lo siguiente:

https://docs.djangoproject.com/en/dev/ref/contrib/csrf/

Cada día me sorprendo más con lo excelente que es este framework, al igual que Python.

De un modo bastante simple previsto por Django se es posible defenderse de éste último tipo de ataque que comente. No soy un experto de en programación web, nunca me ha gustado, pero me pregunto si muchos de los otros frameworks tienen está capacidad.

Fuera del tema, ya empezó el curso de seguridad ofrecido por la universidad de Standford, espero poder dedicarle, ya vi por encima el material que colocaron y se ve full interesante, espero tener el tiempo y las ganas de leerlo, ya veremos!