Reputation: 5655
My django project has a user model which has functionality to do things on the site, like post stuff, comment, edit, etc. I was thinking of ways to limit the number of actions per day to prevent spamming, and instead of getting into annoying datetime related stuff I decided to just store the data into a cache (memcached) to create a functionality for this.
EDIT: Found a solution:
I'm using the old implementation, caching the number of posts a day, with a day set as the timeout. But there also is a celery async cron job which checks and wipes the selected cache keys once a day.
So i'm using the original code I had below, plus this one:
from django.core.cache import cache
from lib.cache_keys import daily_count_key
from django.contrib.auth.models import User
@periodic_task(run_every=crontab(hour='*/24')) # AKA run once a day
def wipeDailyLimits():
users = User.objects.all()
for user in users:
cache.delete(daily_count_key("modelone", user))
cache.delete(daily_count_key("modeltwo", user))
cache.delete(daily_count_key("modelthree", user))
Example:
def post_entry(request):
# post the entry
daily_post_count = cache.get(request.user.id + "entrycount")
if daily_post_count >= 5:
return error
if daily_post_count is None:
daily_post_count = 1
if daily_post_count < 5: # 5 posts per day
daily_post_count += 1
cache.set(request.user.id + "entrycount", daily_post_count , 86400)
# Unix one day time out
# return regular
So far this strategy is looking good and everything works, but I wanted to know if there are any loopholes or tricks that users can possibly use to break this system? And if so, how do most people handle limitations in django?
Thanks
Upvotes: 0
Views: 115
Reputation: 5655
Found a solution that has the benefits of both of your's but is easier to implement. I'm using the old implementation, caching the number of posts a day, with a day set as the timeout. But there also is a celery async cron job which checks and wipes the selected cache keys once a day.
Users will know they are being restricted because just with variables passed into the template.
Thanks to you guys for helping anyways!
Upvotes: 0
Reputation: 9566
I would suggest using a different approach. You current approach means that the user is not aware of whether his post gets across even after being non-spam. Rather if you want to limit, before rendering the page/site, add middleware that does a count()
of whatever you want to constrain on and adds a flag to your HTTPResponse
that states daily_limt_exceeeded=True
. That way you can show an appropriate flag-based warning message and disable the user from posting after the limit is hit.
If you want to you can use the Django Comments Framework for comments on your site, it comes with some spam protection:
Notes on the comment form
The form used by the comment system has a few important anti-spam attributes you should know about: It contains a number of hidden fields that contain timestamps, information about the object the comment should be attached to, and a "security hash" used to validate this information. If someone tampers with this data -- something comment spammers will try -- the comment submission will fail. If you're rendering a custom comment form, you'll need to make sure to pass these values through unchanged. The timestamp is used to ensure that "reply attacks" can't continue very long. Users who wait too long between requesting the form and posting a comment will have their submissions refused.
The comment form includes a "honeypot" field. It's a trap: if any data is entered in that field, the comment will be considered spam (spammers often automatically fill in all fields in an attempt to make valid submissions).
The default form hides this field with a piece of CSS and further labels it with a warning field; if you use the comment form with a custom template you should be sure to do the same. The comments app also depends on the more general Cross Site Request Forgery protection that comes with Django. As described in the documentation, it is best to use CsrfViewMiddleware. However, if you are not using that, you will need to use the csrf_protect decorator on any views that include the comment form, in order for those views to be able to output the CSRF token and cookie.
Upvotes: 1
Reputation: 181077
Two things;
If a user does multiple posts at once, they may get them through since more than one process could read the count, both increase their daily_post_count and both store the old value + 1 (if they ran after each other, they'd store old value + 2 in the end) Not very likely to be a problem.
Your count doesn't reset quite as you want, since you always reset the timeout every time you save a post. If I make one post, then 23 hours later one more, then 23 hours later yet one more, the count would be 3, but 46 hours would have elapsed. The only way to have the counter set to 0 is to wait 24 hours without posting at all, otherwise it will never decrease. Very likely to annoy users if they can't post if they should be able to :)
Upvotes: 1