Python Web Server tornado use summary _python

Source: Internet
Author: User
Tags constant ord prepare uuid wrapper

The first thing to say is its security, this aspect really can let me feel its good hard intentions. This can be divided into two main points:

First, the prevention of cross-station forgery request (Cross-site request forgery, referred to as CSRF or XSRF)

CSRF means simply that an attacker forges a real user to send a request.

For example, suppose a bank Web site has such a URL:
Http://bank.example.com/withdraw?amount=1000000&for=Eve
When the user of the bank website accesses the URL, it will give Eve the user 1 million yuan. The user will not easily click on the URL, but an attacker could embed a fake picture on another site and set the image address to that URL:

Then when the user visits the malicious Web site, the browser will launch a GET request to the URL, so that the user without the knowledge of the case, 1 million was transferred away.

To prevent this attack from being simple, it is not allowed to perform a change operation (such as a transfer) through a GET request. However, other types of requests are also unsafe if an attacker constructs such a form:

Copy Code code as follows:
<form action= "Http://bank.example.com/withdraw" method= "POST" >
<p> forward lottery to send IPad! </p>
<input type= "hidden" name= "Amount" value= "1000000" >
<input type= "hidden" name= "for" value= "Eve" >
<input type= "Submit" value= "Forwarding" >
</form>

The unknown user points to the "forward" button, and the money is turned away ...

To eliminate this situation, you need to add a field that cannot be forged by an attacker when not getting requests, and verify that the field has been modified when processing the request.
Tornado's approach is simple, adding a randomly generated _xsrf field to the request, and adding the field to the cookie, comparing the values of the 2 fields when the request is received.
Since non-site web pages are not able to obtain or modify cookies, this ensures that _XSRF cannot be forged by Third-party Web sites (HTTP sniffing exception).
Of course, users are free to get and modify cookies themselves, but this is no longer part of the CSRF: the user himself to forge their own things, of course, by himself to bear.

To use this feature, you need to add the Xsrf_cookies=true parameter when generating the Tornado.web.Application object, which will give the User a cookie field named _XSRF.
In addition, you need to add xsrf_form_html () to a form that is not a GET request, and if you don't need a Tornado template, you can generate it with self.xsrf_form_html () inside the Tornado.web.RequestHandler.

For AJAX requests, there is basically no need to worry about cross stations, so the previous version of Tornado 1.1.1 does not validate requests with x-requested-with:xmlhttprequest.
Later, Google's engineers pointed out that malicious browser plug-ins can forge Cross-domain AJAX requests, so it should also be validated. I am not noncommittal, because the browser plug-ins can be very large permissions, fake cookies or directly submit the form.
The solution, however, is to simply get the _xsrf field from the cookie, then add the parameter to the AJAX request, or put it in the X-xsrftoken or X-csrftoken request. If you bother, you can use JQuery's $.ajaxsetup () to deal with:

Copy Code code as follows:

$.ajaxsetup ({
Beforesend:function (JQXHR, settings) {
Type = Settings.type
if (type!= ' get ' && type!= ' head ' && type!= ' OPTIONS ') {
var pattern =/(. +; *)? _xsrf *= * ([^; "] +)/;
var xsrf = pattern.exec (Document.cookie);
if (XSRF) {
Jqxhr.setrequestheader (' X-xsrftoken ', xsrf[2]);
}
}
}});

Also, by the way, cross-station scripting (Cross-site scripting, referred to as XSS). In contrast to CSRF, XSS uses the vulnerabilities of the hacked Web site to inject script code that the attacker wants to execute on the site, allowing users browsing the site to execute it.
However, as long as users are not allowed to enter HTML (for example, < and >), the attributes of HTML elements are validated (for example, the quotes in the attribute are escaped, the attributes such as SRC and event handling cannot be filled in with JavaScript code, etc.), and the CSS (including the style attribute ) can be avoided in the expression.

Second, to prevent the counterfeiting of cookies.

The aforementioned CSRF and XSS are the attackers who, without the user's knowledge, use his name to do the operation, while the counterfeit cookie is the attacker's own initiative to forge other users to operate.
For example, suppose that a Web site's login verification is to check the user name in the cookie, as long as it is met, the user is considered logged in. The attacker can then impersonate an administrator by setting a value such as username=admin in the cookie.

To prevent cookies from being forged, you first need to mention two parameters when setting cookies: Secure and HttpOnly. These two parameters are not in the parameter list of Tornado.web.RequestHandler.set_cookie (), but are passed as keyword parameters and are defined in cookie.morsel._reserved.
The former means that the cookie can only be passed through a secure connection (that is, HTTPS), which makes it impossible for the sniffer to intercept the cookie, which requires that it be accessed only under the HTTP protocol (that is, the field in Document.cookie cannot be obtained by JavaScript). And will not be sent to the server via the HTTP protocol after it is set up, which makes it impossible for an attacker to simply spoof cookies through JavaScript scripts.

However, for a malicious attacker, these two parameters do not prevent the cookie from being forged. This requires a cookie to be signed, once modified, the server side can be judged. The Set_secure_cookie () method is provided in the
Tornado to sign cookies. A string of secret keys (the Cookie_secret parameter when generating the Tornado.web.Application object) is required for signing, which can be generated by the following code:
Base64.b64encode (UUID.UUID4). Bytes + uuid.uuid4 (). bytes)
This parameter can be randomly generated, but it is better to share a constant if there are multiple Tornado processes to serve, or sometimes to reboot, and be careful not to divulge it.

This signature is in the HMAC algorithm, the hash algorithm is based on SHA1. Simply put the cookie name, value, and timestamp hash as the signature, and the value | timestamp | signature as the new value. So that the server side as long as the secret key to encrypt again, compare the signature has changed can judge the authenticity.
It is worth mentioning that the read source also found such a function:
def _time_independent_equals (A, B):
    If Len (a)!= Len (b):
         return False
    result = 0
    if Type (a[0) ) is int:  # Python3 byte strings
        to x, y in Zip (A, b):
   ;          result |= x ^ y
    else:  # Python2
        for x, y in Zip (A, b):
             result |= Ord (x) ^ ord (y)
    return result = 0
After reading for half a day, I did not find any advantages to the normal string comparison. Until I read the answer on the StackOverflow that: in order to avoid the attackers through the test comparison time to determine the correct number of digits, this function so that the comparison of the time is relatively constant, also eliminate this situation. (say this answer see I all kinds of admire Ah, do security experts really not I so superficial ... )

Thirdly, succession of tornado.web.RequestHandler.

On the execution process, tornado.web.Application searches for a matching RequestHandler class based on the URL and initializes it. Its __init__ () method calls the Initialize () method, so just overwrite the latter and do not need to invoke the Initialize () of the parent class.
Then, the Get/post () and other methods of the handler are searched according to the different HTTP methods, and the prepare () is run before execution. None of these methods will invoke the parent class voluntarily, so call it yourself if necessary.
Finally, you call the handler finish () method, which is best not overwritten. It calls the On_finish () method, which can be overridden to handle some of the aftermath (such as shutting down a database connection), but no longer sends data to the browser (because the HTTP response has been sent and the connection may have been closed).

By the way, how to handle the error page.
In simple terms, any RequestHandler error is captured by its Write_error () method (in which the prepare (), get () and finish () are executed in the _execute () method that executes the method. So overriding this method can be:

Copy Code code as follows:
Class RequestHandler (Tornado.web.RequestHandler):
def write_error (self, Status_code, **kwargs):
If Status_code = 404:
Self.render (' 404.html ')
Elif Status_code = = 500:
Self.render (' 500.html ')
Else
Super (RequestHandler, self). Write_error (Status_code, **kwargs)

For historical reasons, you can also overwrite the get_error_html () method, but not recommended.
In addition, you may not have the _execute () method of error.
For example, the Initialize () method throws an unhandled exception that is caught by the IOStream, then closes the connection directly and does not output any error pages to the user.
If you don't find a handler that can handle the request, you'll use Tornado.web.ErrorHandler to handle the 404 error. This can replace this class to implement custom error pages:
Copy Code code as follows:
Class Pagenotfoundhandler (RequestHandler):
def get (self):
Raise Tornado.web.HTTPError (404)

Tornado.web.ErrorHandler = Pagenotfoundhandler


Another method is to add a handler that captures any URL at the end of the application handlers parameter:
Copy Code code as follows:
application = Tornado.web.Application ([
# ...
('. * ', Pagenotfoundhandler)
])


Four, then talk about processing login.

Tornado provides @tornado. web.authenticated this adorner, add it before the handler get () method.
It relies on three codes:
You need to define the Get_current_user () method for handler, for example:

Copy Code code as follows:
def get_current_user (self):
Return Self.get_secure_cookie (' user_id ', 0)

When its return value is false, it jumps to the login page.
Set Login_url parameters when creating application:
Copy Code code as follows:
application = Tornado.web.Application (
[
# ...
],
Login_url = '/login '
)

Defines the Get_login_url () method for handler.
If you cannot use the default Login_url parameter (for example, a normal user and an administrator need a different logon address), you can override the Get_login_url () method:
Copy Code code as follows:
Class Adminhandler (RequestHandler):
def get_login_url (self):
Return '/admin/login '

Incidentally, the jump to the login page comes with a next parameter that points to the URL that was accessed before the login. To achieve a better user experience, you need to jump to the URL after you log on:
Copy Code code as follows:
Class Loginhandler (RequestHandler):
def get (self):
If Self.get_current_user ():
Self.redirect ('/')
Return
Self.render (' login.html ')

Def post (self):
If Self.get_current_user ():
Raise Tornado.web.HTTPError (403)
# Check username and password
If success:
Self.redirect (Self.get_argument (' Next ', '/'))


In addition, I use AJAX technology in many places, and the front end is lazy to deal with 403 errors, so I can only change the authenticated ():
Copy Code code as follows:
Def authenticated (method):

@functools. Wraps (method)
def wrapper (self, *args, **kwargs):
If not self.current_user:
If Self.request.headers.get (' x-requested-with ') = = ' XMLHttpRequest ': # JQuery and other libraries will be shipped with this header
Self.set_header (' Content-type ', ' Application/json; Charset=utf-8 ')
Self.write (Json.dumps ({' Success ': False, ' msg ': U ') your session has expired, please login again! '}))
Return
If Self.request.method in (' Get ', ' head '):
url = Self.get_login_url ()
If "?" Not in URL:
If Urlparse.urlsplit (URL). Scheme:
# If login URL is absolute, make next absolute too
Next_url = Self.request.full_url ()
Else
Next_url = Self.request.uri
url = "?" + Urllib.urlencode (Dict (Next=next_url))
Self.redirect (URL)
Return
Raise Tornado.web.HTTPError (403)
Return method (self, *args, **kwargs)
Return wrapper

Five, and then say get the user's IP address.

In short, you can get it with self.request.remote_ip in the handler method.
However, if you use a reverse proxy, you get the IP of the agent, this time you need to create Httpserver to add xheaders settings:

Copy Code code as follows:
if __name__ = = ' __main__ ':
From Tornado.httpserver import Httpserver
From Tornado.netutil import bind_sockets

sockets = Bind_sockets (80)
Server = Httpserver (application, Xheaders=true)
Server.add_sockets (Sockets)
Tornado.ioloop.IOLoop.instance (). Start ()


In addition, I only need to deal with IPV4, but the local test will get:: 1 This IPv6 address, so you need to set up:
Copy Code code as follows:
If settings. Ipv4_only:
Import socket
sockets = Bind_sockets (family=socket.af_inet)
Else
sockets = Bind_sockets (80)

Finally, how to improve the performance in the production environment.

Tornado can create multiple child processes before Httpserver calls Add_sockets () and use the advantages of multiple CPUs to handle concurrent requests.

In simple terms, the code is as follows:

Copy Code code as follows:
if __name__ = = ' __main__ ':
If settings. Ipv4_only:
Import socket
sockets = Bind_sockets (family=socket.af_inet)
Else
sockets = Bind_sockets (80)
If not settings. Debug_mode:
Import tornado.process
Tornado.process.fork_processes (0) # 0 means to create a corresponding number of child processes by number of CPUs
Server = Httpserver (application, Xheaders=true)
Server.add_sockets (Sockets)
Tornado.ioloop.IOLoop.instance (). Start ()

Note that the Autoreload feature cannot be enabled in this way (application the debug parameter cannot be true when it is created).

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.