Commit fdf40df8 authored by o@immerda.ch's avatar o@immerda.ch
Browse files

require pow on first login

parent a1eee6d1
......@@ -41,50 +41,18 @@ class SessionsController < ApplicationController
security_level = security_level(current_user || params[:user_id])
session[:ping_pong] ||= security_level[:redirects]
if params[:pow].present?
# client gives us a captach, let's verify it
unless check_pow
session.delete(:ping_pong)
return login_failed
end
session[:ping_pong] -= 1
unless allowed_user?(params[:user_id])
return login_failed
end
if session[:pre_auth]
load_params
session[:ping_pong] = [session[:ping_pong],0].max
nonce = session[:pow_nonce] = (0...6).map { captcha_chars.sample }.join
@pow_factor = session[:pow_factor] = security_level[:pow_factor]
if session[:ping_pong] == 1 && security_level[:enable_captcha]
@captcha = Base64.encode64(SimpleCaptchaReloaded::Image.new.generate(nonce))
else
# Add some obfuscated script to the page that will fill in the nonce
# field, compute the pow and submit it. The obfuscation makes it
# harder for an attacker to get the nonce without executing this
# javascript (thus running a browser) first.
script = <<EOF
window.addEventListener('load', function() {
function d() { eval("debugger"); setTimeout(d, 40);};
setTimeout(d, 10);
try {
console.log('login');
if (!navigator.webdriver) {
document.getElementById('pow_nonce').value = '#{nonce}';
brf();
document.forms[0].submit();
}
} catch(e){ }
})
EOF
@pow_nonce_script = JSObfu.new(script).obfuscate.to_s.html_safe
end
sleep security_level[:wait]
if session[:ping_pong] == 0
session.delete(:ping_pong)
else
flash[:notice] = nil
return render 'captcha'
end
if !params[:pow].present?
return login_failed
end
# client gives us a captach, let's verify it
unless check_pow
session.delete(:ping_pong)
return login_failed
end
# username is a honeypot field
......@@ -98,8 +66,16 @@ EOF
return render 'new'
end
unless allowed_user?(params[:user_id])
return login_failed
if session[:ping_pong] > 0
session[:ping_pong] -= 1
load_params
load_pow(true)
sleep security_level[:wait]
flash[:notice] = nil
return render 'captcha'
else
session[:ping_pong] = 0
session.delete(:ping_pong)
end
h = params[:handoff]
......@@ -174,6 +150,7 @@ EOF
logger.error "Auth failed. This client has now #{client_fails} failed logins"
Rails.cache.write(client_auth_key, client_fails+1, expires_in: 10.minutes)
load_params
load_pow(false)
render 'new'
end
......@@ -186,6 +163,7 @@ EOF
flash[:notice] = nil
session[:pre_auth] = true
load_params
load_pow(false)
end
private
......@@ -252,6 +230,40 @@ EOF
end
end
def load_pow(autosubmit)
security_level = security_level(current_user || params[:user_id])
nonce = session[:pow_nonce] = (0...6).map { captcha_chars.sample }.join
@pow_factor = session[:pow_factor] = security_level[:pow_factor]
if session[:ping_pong] == 1 && security_level[:enable_captcha]
@captcha = Base64.encode64(SimpleCaptchaReloaded::Image.new.generate(nonce))
else
# Add some obfuscated script to the page that will fill in the nonce
# field, compute the pow and submit it. The obfuscation makes it
# harder for an attacker to get the nonce without executing this
# javascript (thus running a browser) first.
autosubmit_str =
if autosubmit
"brf();document.forms[0].submit();"
else
""
end
script = <<EOF
window.addEventListener('load', function() {
function d() { eval("debugger"); setTimeout(d, 40);};
setTimeout(d, 10);
try {
console.log('login');
if (!navigator.webdriver) {
document.getElementById('pow_nonce').value = '#{nonce}';
#{autosubmit_str}
}
} catch(e){ }
})
EOF
@pow_nonce_script = JSObfu.new(script).obfuscate.to_s.html_safe
end
end
def webmail_url
if request.host =~ /ysp4gfuhnmj6b4mb\.onion/
"https://webmail.ysp4gfuhnmj6b4mb.onion/"
......
......@@ -54,9 +54,15 @@
<%= hidden_field('', :unlock, :value => @unlock) %>
<%= hidden_field('', :horde_select_view, :value => @horde_select_view) %>
<%= hidden_field('', :username) %>
<%= hidden_field(:pow, :factor, :value => @pow_factor) %>
<%= hidden_field(:pow, :nonce) %>
</p>
<% end %>
<script type="text/javascript">
<%= @pow_nonce_script %>
</script>
<% if @news_frame %>
<hr style="margin-top:30px"/>
<div class="news">
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment