Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Login Takes Too Much Time. #1207

Closed
lfuelling opened this issue Oct 21, 2013 · 43 comments
Closed

Login Takes Too Much Time. #1207

lfuelling opened this issue Oct 21, 2013 · 43 comments

Comments

@lfuelling
Copy link

When I log in (or sign up) on my Ghost installation, it takes up to three minutes until the page reloads and I'm logged in.

I use Ghost on my Raspberry Pi (not the most powerful device, I know) and I don't think this should last that long.

@jayay
Copy link

jayay commented Oct 21, 2013

Yeah, same here. node index slows down the pi.

@lfuelling
Copy link
Author

The strange thing is that this only affects the sign up/login process. Creating a post or updating an image works normal.

@jayay
Copy link

jayay commented Oct 21, 2013

Exactly. CPU usage increases to 100 percent at login/server start.

@sebgie
Copy link
Contributor

sebgie commented Oct 21, 2013

Just an uneducated guess, but is it possible that this is caused due to cryptographic operations during signup/login/server start?

@lfuelling
Copy link
Author

Yes, this is what I think. But which cryptographic operations in a login operation need to take 3 Minutes? That's a bit long...

@sebgie
Copy link
Contributor

sebgie commented Oct 21, 2013

As far as I know, there are no exotic operations involved. Hash calculation for passwords (bcrypt-nodejs) and encryption of sessions (express).

@jgillich
Copy link
Contributor

Anther one having that issue.

But I don't think this is directly related to the Pi's performance. I run Ghost on a rather slow Celeron 1007U with 1.5 GHz and login is super quick (~1 sec).

What OS do you run on your Pi? Maybe you can compare your setup with @halfdan, I guess he had no such issues. :)

I will also try Ghost on my Cubieboard soon, might as well be related to the ARM architecture.

@jayay
Copy link

jayay commented Oct 23, 2013

I'm running it on Raspbian. But I think IT might be caused by SQLite. I'll try it with MySQL.

@lfuelling
Copy link
Author

I'm using Raspbian, the Hard-Float version. I thought SQLite was a bit perfomance friendlier compared to MySQL...

@halfdan
Copy link
Contributor

halfdan commented Oct 23, 2013

@jgillich I actually have the same issues, just didn't bother to look into it yet. I'll check what's happening later today and provide a fix if possible.

@halfdan
Copy link
Contributor

halfdan commented Oct 23, 2013

The issue really comes from bcrypt being horribly slow on the Raspberry Pi. I think there's not much you can do about it since both the bcryptjs and nodejs-bcrypt libraries are relatively slow (as intended by bcrypt).

@ErisDS
Copy link
Member

ErisDS commented Oct 24, 2013

Thanks for looking into this @halfdan

Unfortunately this means there is no bug to be resolved. The slow speed of bcrypt is completely intentional, so if nothing else is slowing things down there's nothing to fix.

@ErisDS ErisDS closed this as completed Oct 24, 2013
@mirceanis
Copy link

I managed to get much better performance using bcrypt instead of bcrypt-nodejs.
The API is almost the same so there are very few changes to make in core/server/models/user.js

@ErisDS
Copy link
Member

ErisDS commented Nov 18, 2013

@mirceanis Thanks for this useful piece of info.

It makes sense that bcrypt would be faster as it is a binding for the C++ module, rather than a JavaScript implementation. Unfortunately this is also the reason why we can't use bcrypt in the core of Ghost - it requires anyone installing Ghost to compile a native binary for that module, which requires a number of complex dependencies and would be a significant barrier to the average person trying to install Ghost.

However, what we can do is consider how we can make bcrypt easily replaceable with an App so that people who are comfortable building a binary can benefit from the improved performance of bcrypt.

@mirceanis
Copy link

Yeah, I figured that would be the case but I felt that maybe that info would benefit people who are running Ghost on very slow machines (like I am). On my Raspberry Pi it takes minutes to do a password compare with the pure javascript implementation of bcrypt and it's almost instant with the c++ module.

@ErisDS
Copy link
Member

ErisDS commented Nov 18, 2013

@mirceanis Definitely interesting - well worth considering how we can make this configurable with an App.

@halfdan
Copy link
Contributor

halfdan commented Nov 18, 2013

This really defeats the point of using bcrypt. Bcrypt is supposed to take an significant amount of time to compute a hash in order to prevent/slow down brute force attacks.

What you can do however is decrease the number of rounds (currently by default at 2^10).

@mirceanis
Copy link

Even with reduced number of rounds, bcrypt-nodejs is increddibly slow.
If the purpose is to slow down brute-force attacks, you can easily setTimeout() to a controllable delay.

@halfdan
Copy link
Contributor

halfdan commented Nov 18, 2013

@mirceanis You're missing the point. setTimeoutwouldn't do any good here - it should be hard to find the password even if you have the hash. By using more rounds you make it hard for an attacker to brute force the hash even when he's running it on his own machine.

@mirceanis
Copy link

@halfdan I don't get it.
You were the one suggesting a smaller number of rounds.
What I tried to say was that bcrypt-nodejs is terribly slow even with reduced number of rounds but bcrypt is fast even with the default number of rounds.
AND, if the attacker already has your hash, he most certainly would NOT use bcrypt-nodejs to brute-force it.

My suggestion for require('bcrypt') + setTimeout() was to create a "tar pit" for anyone trying different passwords remotely.
In this case, the server would not waste CPU trying to compare brute-force passwords, It would do the computation fast and then delay the response. So if a burst of password attempts was made, the server would respond with "Too many logins".

@halfdan
Copy link
Contributor

halfdan commented Nov 18, 2013

There already is a brute force protection in place - limiting the amount of requests that are processed.

We are talking about two different things here:

  • The reason why bcrypt by design is computationally expensive.
  • Why we can't use the bcrypt module in Ghost

@ksmithbaylor
Copy link

I am having the same problem, and I traced it even further into the bcrypt-nodejs code. It seems that it is hanging in the decode_base64 function just after getting the character code for the unicode character represented by 65518. I don't know if this is just because of the particular password that I used, but I hope it helps somebody. I'm going to try to get the other bcrypt library working instead.

@ghost
Copy link

ghost commented Jan 3, 2014

I'm encountering this issue too, on a raspberry pi. I hope there will be a workaround/fix for this, because I find this platform really beautiful/endearing, but I'm unable to use it so far.

@ksmithbaylor
Copy link

I couldn't figure out what the problem with bcrypt-nodejs is, but I made a temporary fix by using sha1 for user password encryption instead. Here's the gist: https://gist.github.com/ksmithbaylor/8232988. It runs very fast on my raspberry pi, and should be relatively secure. All I did was hash the password with sha1 instead of bcrypt.

@ErisDS
Copy link
Member

ErisDS commented Jan 3, 2014

There is, to my knowlege, no problem with bcrypt-nodejs, as mentioned in previous comments, bcrypt is intentionally slow - it is a security feature.

bcrypt is delibrately slow and resource intensive to try to make brute forcing more difficult, the JS-based implementation Ghost uses is even more slow and intensive.

What's happening is you're running an incredibly complex cryptographic operation on sub-standard hardware. Don't get me wrong, I love the Pi as much as the next person, but it is much less powerful than an average computer.

sha1 is not secure and should not be used for hashing passwords

Someone can use the might of amazon ec2 (another great way of running Ghost btw) and brute-force a sha1 hash of an 8-char password in less time than you are reporting it takes to log in with bcrypt.

@voronoipotato
Copy link

@mirceanis you seem to misunderstand the premise of having a more complicated hash. The higher bcrypt value is not to prevent brute force log-ins (although that is a side effect). The point of having a higher bcrypt value is that if your database is compromised, when someone tries to rainbow table, or crack passwords in your database it takes longer to generate those values. If you keep that value high enough it can be prohibitively expensive to do so (millions or even billions of dollars). @ksmithbaylor you would be better off simply reducing your bcrypt cost.

@ErisDS bcrypt is intentionally slow, but having it be slower without adding complexity does nothing, is there perhaps an optional DLL dependency? The thing is that an attacker will have a very, very fast bcrypt implementation (GPU etc), so every bit of speed counts.

@ErisDS
Copy link
Member

ErisDS commented Jan 3, 2014

@voronoipotato Ghost is using the js implementation because C++ binaries are impossible for the average user to install. If you want to use a different implementation of bcrypt feel free to hack Ghost, but introducing this for everyone does not make sense.

@voronoipotato
Copy link

@ErisDS Fair, I believe the team that is creating your library is working on some degree of parallelization so that will cause a speed increase. Is there a way in the set up process to change the bcrypt cost? I believe that would solve most of the average user's struggles, and would provide the ability for them to have higher security if they felt they were a higher profile target.

@ksmithbaylor
Copy link

I understand that bcrypt is intentionally slow. However, the problem is not its speed, but the fact that on ARM hardware, this particular implementation seems to hang for an arbitrary amount of time (up to 75s on my machine) during one iteration of the loop in decode_base64 just after executing the charCodeAt function and returning from getByte. The rest of the algorithm runs perfectly fine, and indeed other implentations of bcrypt would not be orders of magnitude faster if it were not an issue with this particular implantation.

Sha1 was not a good choice, I agree. I am going to replace my example with a different bcrypt library later today.

@mirceanis
Copy link

@voronoipotato You have completely misunderstood what I said. I was offering a solution to a problem.
I'm not criticizing bcrypt as an algorithm and I am not suggesting the use of lighter encryption, not even less number of rounds with bcrypt.

All I'm saying is that the pure javascript implementation is too slow for cheap hardware like the raspberry PI. It takes 2 minutes to verify a password and during that time, CPU usage is at 100%.
My suggestion for people encountering that problem was to use the C++ implementation of bcrypt which has a similar API and is much faster (milliseconds instead of minutes).

At the same time, I understand the need to avoid "complicated" dependencies so @ErisDS has a point.

@ksmithbaylor
Copy link

It seems I must apologize for being wrong. I just looked into it and you are 100% correct, @ErisDS. Chalk it up to my inexperience with node and debugging asynchronous code, but all the other pure javascript implementations of bcrypt performed about the same.

I'm still confused about your claims that the C++ implementation (at https://npmjs.org/package/bcrypt) is hard to install for the average user. I thought npm took care of building the binaries for any dependencies. Running 'npm install bcrypt' seems to compile the code just fine and let me use it out of the box. Am I missing something?

@voronoipotato
Copy link

@ksmithbaylor Windows users will need visual studio configured, also the installation of OpenSSL etc.

@ErisDS
Copy link
Member

ErisDS commented Jan 3, 2014

  • windows users need what @voronoipotato said and more
  • mac users have to have the xcode cli tools installed
  • linux users also need several libraries which are installed on most distros, but not always.

Before Ghost was public, when there were only ~200 users, sqlite3 didn't have pre-built binaries. It was hell to install, for all users on all systems except for the luckiest developers who already had everything. It is vastly improved now but still not as good as I'd like.

@joelekstrom
Copy link

I too had this issue and would like to emphasize once again how much slower the JS version of bcrypt actually is. I'm having a hard time accepting that taking 50+ seconds compared to the few milliseconds of a native binary is an "intended" slowdown. The Pi might be slower, but it isn't that slow (which is proved by using a native binary). This is much more likely due to an edge issue with JS optimization on ARM architectures.

I'm pressing for this issue to be opened again. It's not a direct issue with Ghost itself, but rather an issue with the JS implementation of bcrypt used. Or, since apparently more bcrypt implementations in JS have the same problem, a block of JS used by all these implementations that is seriously bogged down on ARM architectures.

And while this may not directly be an issue with Ghost, it's directly affecting its end users, and if it's not solvable maybe a solution could be proposed when installing or something like that. The binary version may be harder to install, but better that than users abandoning Ghost since they think the admin login is broken.

@joelekstrom
Copy link

Also, maybe I was lucky for having correct dependencies - but for all you others trying to fix this, this solved it for me:

npm install bcrypt

In core/server/models/user.js, change

bcrypt         = require('bcryptjs')

into:

bcrypt         = require('bcrypt')

@ErisDS
Copy link
Member

ErisDS commented Jan 13, 2014

The binary version may be harder to install, but better that than users abandoning Ghost since they think the admin login is broken.

Installing the binary would affect every single Ghost user, many of whom have no technical knowledge. The issue with slow down on login affects just a few people who by their own selection have at least some technical knowledge. It really isn't that hard to swap out the library as you have discovered.

@joelekstrom
Copy link

Installing the binary would affect every single Ghost user, many of whom have no technical knowledge. The issue with slow down on login affects just a few people who by their own selection have at least some technical knowledge. It really isn't that hard to swap out the library as you have discovered.

Yes, I'm not suggesting this is applied for all Ghost installations, but rather that maybe the installer could drop a hint about it if it detects an ARM architecture or something like that. This will not only be a niche problem for Raspberry Pi in the near future as ARM hardware is starting to be used in servers.

@ImTheDeveloper
Copy link

@accatyyc your suggested fix works perfect on the Raspberry Pi for me.

pi@raspberrypi ~/ghost $ sudo npm install bcrypt
npm http GET https://registry.npmjs.org/bcrypt
npm http 200 https://registry.npmjs.org/bcrypt
npm http GET https://registry.npmjs.org/bcrypt/-/bcrypt-0.7.7.tgz
npm http 200 https://registry.npmjs.org/bcrypt/-/bcrypt-0.7.7.tgz
npm http GET https://registry.npmjs.org/bindings/1.0.0
npm http 200 https://registry.npmjs.org/bindings/1.0.0
npm http GET https://registry.npmjs.org/bindings/-/bindings-1.0.0.tgz
npm http 200 https://registry.npmjs.org/bindings/-/bindings-1.0.0.tgz

> bcrypt@0.7.7 install /home/pi/ghost/node_modules/bcrypt
> node-gyp rebuild

gyp WARN EACCES user "root" does not have permission to access the dev dir "/root/.node-gyp/0.10.24"
gyp WARN EACCES attempting to reinstall using temporary dev dir "/home/pi/ghost/node_modules/bcrypt/.node-gyp"
gyp http GET http://nodejs.org/dist/v0.10.24/node-v0.10.24.tar.gz
gyp http 200 http://nodejs.org/dist/v0.10.24/node-v0.10.24.tar.gz
make: Entering directory `/home/pi/ghost/node_modules/bcrypt/build'
  CXX(target) Release/obj.target/bcrypt_lib/src/blowfish.o
  CXX(target) Release/obj.target/bcrypt_lib/src/bcrypt.o
  CXX(target) Release/obj.target/bcrypt_lib/src/bcrypt_node.o
  SOLINK_MODULE(target) Release/obj.target/bcrypt_lib.node
  SOLINK_MODULE(target) Release/obj.target/bcrypt_lib.node: Finished
  COPY Release/bcrypt_lib.node
make: Leaving directory `/home/pi/ghost/node_modules/bcrypt/build'
bcrypt@0.7.7 node_modules/bcrypt
└── bindings@1.0.0

@julianlam
Copy link

Issue opened upstream. For reference, the NodeBB team ran into this recently. So kudos to you guys for doing the legwork to find that using the bcrypt package could be an acceptable workaround.

We also believe in pure-js dependencies, so if it could be fixed upstream, that would be best for us all.

@ofstudio
Copy link

Installing bcrypt completely solves this problem on Raspberry Pi (actually npm install bcrypt takes less time than single login using bcryptjs).

It would be nice if using bgcrypt instead of bcryptjs will be default behavior on ARM systems.

@Dygear
Copy link

Dygear commented Jan 21, 2015

It's still the case the bcrypt is not compiled on ARM system. This really needs to happen, otherwise ghost is useless on ARM platforms.

@halfdan
Copy link
Contributor

halfdan commented Jan 22, 2015

@Dygear I am sorry you are having trouble. bryptjs however relies only on JavaScript and therefore works on any ARM platform. The only reason why it is slow on the Raspberry Pi is because the Pi lacks a proper FPU (but you can speed things up by replacing bcryptjs with the native brypt library as pointed out by @ofstudio). Ghost certainly isn't useless on ARM platforms.

@Zepmann
Copy link

Zepmann commented Jul 14, 2016

The installation and configuration of bcrypt to replace bcryptjs as described in this comment is now mandatory when Ghost is hosted on a Raspberry Pi 1. With bcryptjs the login procedure will timeout with a 'There was a problem on the server' error. This was tested on stock CPU speed with Ghost 0.8.0-1 from the Arch Linux User Repository and nginx 1.10.1 as a reverse proxy server.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests