I am running a django app with a REST API and I am protecting my API endpoint with a custom permission. It looks like this:
class MyPermission(permissions.BasePermission):
def has_permission(self, request, view):
host = request.META.get('HTTP_HOST', None)
return host == "myhost.de"
)
The idea is that my API is only accessible via "myhost.de".
Right now I am testing this with pytest. I can set my headers with:
#pytest.fixture()
def request_unauth(self, client):
result = client.get(
"myurl",
headers={'HTTP_HOST', 'myhost.de'},
content_type="application/json",
)
return result
def test_host(request_unauth):
assert request_unauth.status_code == 200
Since I can easily fake my headers I assume that this might also be easily done with other tools? How can MyPermission be evaluated from a security perspective?
Thanks so much for any help and hints. Very much appreciated.
Checking the Host header like that does not make sense, and will not protect against 3rd party clients as you described in comments. An attacker can create an arbitrary client and send request to your API, and that request can (and will) include the correct Host header as any other legitimate request.
Also based on your comments, you want to authenticate the client application, which is not technically possible, as it has been discussed many times. With some work (the amount of which you can influence somewhat) anybody can create a different client to your API, and there is no secure way you could prevent that, because anything you include in your client will be known to users (attackers), and will allow them to copy it. You can and probably should authenticate your users though, check access patterns, implement rate limiting, revoke user access in case of suspicious activity and so on - but this is all based on user authentication.
You can also prevent access from a client running in a standard browser on different domain, by sending the correct CORS headers (or not sending CORS headers at all) in your API.
Related
If I have a server end point that say does a simple task like initializes an API with a token that I generate client side, and then print the users account info.
Can I initialize() the API globally so the user can do other tasks after printing account info?
and
How does that affect other users initializing() and printing info if they do it at the same time?
I don't understand how this server works and any help would be great. Thank you!
If I'm understanding you correctly, it sounds like the easiest way to accomplish what you're trying to do is to use the flask-login module.
You will want to create an endpoint / Flask route (for example, '/login') that the user will send a POST request to with a username (and usually also a password). When the login is successful, the user's browser will have a cookie set with a token that will allow them to access Flask routes that have a #login_required decorator attached.
In your Python code for those routes, you will be able to access the "current_user", which will allow you to tailor your response to the particular user sending the request.
This way of doing things will allow you to deal with your first question, which seems to be about saving a user's identity across requests, and it will also allow you to deal with your second question, which seems to be about the problem of having different users accessing the same endpoint while expecting different responses.
Nathan's answer points you in the right direction, but I'll make a comment about state and scope because you asked about multiple users.
Firstly, the normal way to do this would be for the server to generate the random token and send it back to the user.
client request 1 > init > create token > store along with init data > return token to user
client request 2 > something(token) > find data related to token > return data to user
When you run Flask (and most webapps) you try to make it so that each request is independent. In your case, the user calls an endpoint and identifies themselves by passing a token. If you make a record of that token somewhere (normally in a database) then you can identify that user's data by looking up the same token they pass on subsequent requests.
Where you choose to store that information is important. As I say a database is the normal recommended approach as it's built to be accessed safely by multiple people at the same time.
You might be tempted to not do the database thing and actually store the token / data information in a global variable inside python. Here's the reason why that's (probably) not going to work:
Flask is a wsgi server, and how it behaves when up and running depends on how it's configured. I generally use uwsgi with several different processes. Each process will have its own state that can't see one another. In this (standard / common) configuration, you don't know which process is going to receive any given request.
request 1 > init(token) > process 1 > store token in global vars
request 2 > other(token) > process 2 > can't find token in global vars
That's why we use a database to store all shared information:
request 1 > init(token) > process 1 > store token in db
request 2 > other(token) > process 2 > can find token db
I have received the task of adding HTTP authentication(BasicAuthAuthenticationPolicy) to a Pyramid application that already have a AuthTktAuthenticationPolicy implemented...
Basically I need to create a RESTful API to authenticate users(Could I use BasicAuthAuthenticationPolicy for that?).
Is there a way to check whether a user is using the web interface, or using the api - to check which Authentication Policy to use?
I have not come across documentation that covers two different Authentication Policies in a single Pyramid application(if its even possible).
PS:
I have come across a blog series that started showing how to create a RESTful API with the pyramid framework... The Blogger reported that there was going to be 6 articles in the sersies, however I only managed to find two of those articles: Building a RESTful API with Pyramid - Setup and Building a RESTful API with Pyramid - Resource and Traversal. I am/was looking forward to his last article: Building a RESTful API with Pyramid - Authentication and ACL, but it doesn't seem like he is going to finish the series.
To recap my questions:
Could I use BasicAuthAuthenticationPolicy for building a RESTful api to authenticate users?
Is there a way to check whether a user is using the web interface, or using the API - to check which Authentication Policy to use?
Any help would be Appreciated.
So what I did was I merged Pyramids BasicAuthAuthenticationPolicy and CallbackAuthenticationPolicy and I ended up with this.
I have modified the callback method to use a redis session
To use this class(HTTPAthentication) you can do something like(this is an example how I implemented it for my usecase):
def _get_userid(details, request):
userre = re.compile("""^([a-zA-Z1-9\.]+):([a-zA-Z1-9\.:-]+)$""", re.I)
userpass = base64.b64decode(details[1])
res = userre.search(userpass)
if res:
return res.group()[0]
def authcheck(username, password, request):
user = Users.by_username(username, enabled=True)
host = request.registry.settings.get("authentication.host", "127.0.0.1")
maxattempts = request.registry.settings.get("authentication.maxattempts",5)
base = _get_userid(request.authorization, request)
if request.redis.exists("pimssess:%s" % base64.b64encode(request.remote_addr+":"+base)):
store = pickle.loads(request.redis.get("pimssess:%s" % base64.b64encode(request.remote_addr+":"+base)))
if store.get("auth.attempts").get(request.remote_addr):
if store["auth.attempts"][request.remote_addr] >= maxattempts:
raise HTTPMethodNotAllowed(body="You have been locked out for 5 minutes")
if user and user.agent and not user.is_http_auth:
raise HTTPMethodNotAllowed(body="You are not permitted http access")
if user and user.agent and user.host != host:
raise HTTPMethodNotAllowed(body="Your host is not permitted http access")
if user and user.agent and not user.validate_password(password):
time.sleep(1.5)
raise HTTPForbidden(body="Failed login, Incorrect password")
return getGroups(username)
The getGroups function retruns a list of groups that is attached to the user , i.e. ['admin', 'reporting']
I followed this example: BasicAuthAuthenticationPolicy(scroll to bottom)
And for the web interface login(CallbackAuthentication), you create a login interface, and create the view to accommodate the template(checking for password and username match, etc.)
Oh I almost forgot... In your project __init__.py, when you call the AuthPolicy, in the def main(...). I did:
authentication = AuthPolicy(secret='##^&*$!DSYUIDSA8321789DS',
hashalg='sha512', check=authcheck, debug=True)
authorization = ACLAuthorizationPolicy()
config = Configurator(settings=settings, root_factory=RootFactory,
authentication_policy=authentication,
authorization_policy=authorization)
I do hope this can help someone.
Pyramid does not make it easy to use different policies for different parts of the app (maybe something to work around with custom decorators) but for multiple policies check out pyramid_authstack. I'm using it with Session and BasicAuth policies for the same purpose as you are.
If it's not straightforward having one pyramid app with two auth policies you can have TWO separate Pyramid apps with a different policy each assembled into a single WSGI stack. Each app can import the same Python code, so, essentially, it'll be two startup files using the same views and everything.
If your apps have different URLs you can use paste.urlmap for this, and if your requirements are more complex you can even write your own router (say, requests with a certain HTTP header are routed to one app and without it to another)
My organization uses OpenAM SSO for authentication and my app is written in Pyramid.
The user id will be passed in HTTP header. I can also configure it to pass groups and permissions as well which I can use in acl. This makes the authentication in pyramid redundant. Is it possible to do away with Authenticaion Policy altogether and go with authorization alone?
You need a way to tell pyramid's authorization system who the person is (their effective principals). That is the responsibility of the authentication policy, even if it's something as simple as parsing a header.
class CustomAuthenticationPolicy(object):
def effective_principals(self, request):
principals = [Everyone]
identity = request.headers.get('x-identity')
# validate the identity somehow
if is_valid(identity):
principals += [Authenticated, identity, 'g:editors']
return principals
config.set_authentication_policy(CustomAuthenticationPolicy())
I'm using an existing database for my latest Django project, so unless I change my Models or the Django auth code, it's going to be rather difficult to merge the two.
Rather than messing with the existing auth backend, I'm planning on just writing my own authentication app.
Anyway, all of my previous authentication apps have been written in PHP, where basically i just throw everything in session variables and verify them on every page... Here's what I'm a bit confused about. It appears that when a user is authenticated/logged in, the entire user is added to a session, but I can't figure out where or how that is occurring.
In the default Django login function, it's assigning user to request.user ... is this being saved as a session variable somehow or is it just passed into the next view? If it is just being passed to the next view, how are future requests authenticated without requiring further login requests?
The default Django auth login is below..
def login(request, user):
"""
Persist a user id and a backend in the request. This way a user doesn't
have to reauthenticate on every request.
"""
if user is None:
user = request.user
# TODO: It would be nice to support different login methods, like signed cookies.
if SESSION_KEY in request.session:
if request.session[SESSION_KEY] != user.id:
# To avoid reusing another user's session, create a new, empty
# session if the existing session corresponds to a different
# authenticated user.
request.session.flush()
else:
request.session.cycle_key()
request.session[SESSION_KEY] = user.id
request.session[BACKEND_SESSION_KEY] = user.backend
if hasattr(request, 'user'):
request.user = user
user_logged_in.send(sender=user.__class__, request=request, user=user)
I also tried to follow the user_logged_in.send(), which is in django.dispatch.dispatcher.send but I'm not entirely sure what that's supposed to do either.
def send(self, sender, **named):
"""
Send signal from sender to all connected receivers.
If any receiver raises an error, the error propagates back through send,
terminating the dispatch loop, so it is quite possible to not have all
receivers called if a raises an error.
Arguments:
sender
The sender of the signal Either a specific object or None.
named
Named arguments which will be passed to receivers.
Returns a list of tuple pairs [(receiver, response), ... ].
"""
responses = []
if not self.receivers:
return responses
for receiver in self._live_receivers(_make_id(sender)):
response = receiver(signal=self, sender=sender, **named)
responses.append((receiver, response))
return responses
Basically what I'm looking for is for someone to explain an efficient way to save user session data in Python that does not depend on the Django framework. A little run-through of the Django authentication would be nice as well.
HTTP is stateless; regardless of the server used, the framework or language, there is no intrinsic way for an HTTP client to say "this request is part of that session". That's part of the design of HTTP.
So sessions are always a feature of the web application; either supported by the a web app framework or implemented in the app itself. The most usual way for a stateful session to be created from the stateless protocol is with cookies; Clients will store cookies at the request of a server and return those same cookies to that server in future requests.
Session data can be serialized and stored in the cookie itself, but that's both insecure (secret information could be forged or eavesdropped), and inefficient (takes bandwidth even though the individual bytes are of no use to the client), and so the preferred solution is to use an opaque (and even better, single use) session key is stored in a cookie, and the web application will store session data out of band; either in memory, in the filesystem, or a database backend, or some other option.
django takes care of most of this transparently in "middleware", modules that modify incoming requests and outgoing responses. The auth middleware will read a cookie and check if that represents a logged in user, and if so, add a user object to the request; it also attaches cookies to responses when a user gets logged in. the session middlware works in a similar fashion, checking for a cookie, and reading the session data from wherever it was stored between requests, and also grabbing session data from responses and storing them, along with setting a cookie to associate the client's session with the session data it just stored.
Since both of these features are useful, independent of each other (I tend to avoid sessions, but usually use some kind of authentication), they do not depend on each other. Session-like authentication is implemented in a similar manner as sessions, but authenticated users are not stored in "The Session", nor are sessions attached to "The authenticated User".
You might not think so, but django's authentication system is designed to be extended; if you already have a database of valid users you'd like to authenticate against, it's very simple to add a new auth backend that dovetails neatly into the standard django auth application (which means you can also use other applications that depend on it in turn).
I'm building an application in which the client pings the server every now and then (let's not get into why). When the server handles these requests, it checks whether the client is logged in or not using request.user.is_authenticated()
It looks something like this:
def handle_ping_request(request):
if request.user.is_authenticated():
# Do something...
else:
# Do Something else...
I've noticed that sometimes the server receives a log-in request immediately followed by a ping request (from the same user). The client is then successfully logged-in, the response returns with a new session ID (of the logged in user) and (I guess that) the old session-ID (of the anonymous user) is removed. When the ping request is processed, its request contains the old session-ID. Thus the ping request returns with a third session ID and on the next request the client makes, the client is no longer logged in.
My log-in code looks something like:
if not request.user.is_authenticated():
user = auth.authenticate(...credentials...)
if user and user.is_active:
auth.login(request, user)
Do you have any suggestions on how to avoid this problem? Preferably without involving the client.
Thanks.
It's probably too messy to handle this on the server because you'll have to create some kind of a semaphore system that will also try to guess if any ping is currently from a client that is also being authenticated. My suggestion would be simply to change the client code not to ping while it's waiting for a response to its login request.
You could create an alternative to the standard contrib.auth.login method that keeps the same session id, rather than generating a new one, either by using a custom authentication backend that doesn't generate a new key, or by creating a custom session backend that overrides the cycle_key() method in contrib.sessions.base to reuse the same key.
BUT: think about what you might be opening yourself up to by reusing the same session key - depending on where this system is in use, you'd be making yourself more open to session hijacking (ie: there's only one session id to sniff), as well as potentially issues where caches may return the unauth page content instead of the auth page content because the sessionid is technically the same and the cache can't tell the difference between the two situations, etc, etc, etc.
In short: there's a reason it's built to work, by default, the way it does.