Skip to content
Browse files

Changed format of user file for more security.

Instead of storing a single static salt, we now randomly
generate a separate salt for each user, and store the salt
with the hashed password.

Note:  gitit users who upgrade to this version will have
to delete their gitit-users file and regenerate it by
having users create their accounts again.
  • Loading branch information...
1 parent 8ece19f commit 5a0bee3e616f8fa7168cb9644da34f800dca554e @jgm committed Dec 30, 2008
Showing with 38 additions and 20 deletions.
  1. +3 −7 Gitit.hs
  2. +33 −5 Gitit/State.hs
  3. +0 −5 README.markdown
  4. +0 −1 data/SampleConfig.hs
  5. +2 −2 gitit.cabal
View
10 Gitit.hs
@@ -40,11 +40,9 @@ import qualified Text.XHtml as X ( password, method )
import Data.List (intersect, intersperse, intercalate, sort, nub, sortBy, isSuffixOf)
import Data.Maybe (fromMaybe, fromJust, mapMaybe, isNothing)
import Data.ByteString.UTF8 (fromString, toString)
-import qualified Data.ByteString.Lazy.UTF8 as L (fromString)
import Codec.Binary.UTF8.String (decodeString, encodeString)
import qualified Data.Map as M
import Data.Ord (comparing)
-import Data.Digest.Pure.SHA (sha512, showDigest)
import Paths_gitit
import Text.Pandoc
import Text.Pandoc.ODT (saveOpenDocumentAsODT)
@@ -968,9 +966,7 @@ loginUser page params = do
let uname = pUsername params
let pword = pPassword params
let destination = pDestination params
- cfg <- query GetConfig
- let passwordHash = showDigest $ sha512 $ L.fromString $ passwordSalt cfg ++ pword
- allowed <- query $ AuthUser uname passwordHash
+ allowed <- query $ AuthUser uname pword
if allowed
then do
key <- update $ NewSession (SessionData uname)
@@ -1043,8 +1039,8 @@ registerUser _ params = do
, (not (null fakeField), "You do not seem human enough.") ] -- fakeField is hidden in CSS (honeypot)
if null errors
then do
- let passwordHash = showDigest $ sha512 $ L.fromString $ passwordSalt cfg ++ pword
- update $ AddUser uname (User { uUsername = uname, uPassword = passwordHash, uEmail = email })
+ user <- liftIO $ mkUser uname email pword
+ update $ AddUser uname user
loginUser "/" (params { pUsername = uname, pPassword = pword, pEmail = email })
else formattedPage (defaultPageLayout { pgShowPageTools = False, pgTabs = [], pgTitle = "Register for an account" })
page (params { pMessages = errors }) regForm
View
38 Gitit/State.hs
@@ -22,7 +22,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
{- Functions for maintaining user list and session state.
Parts of this code are based on http://hpaste.org/5957 mightybyte rev by
- dbpatterson -}
+ dbpatterson.
+-}
module Gitit.State where
@@ -33,6 +34,9 @@ import Data.Generics hiding ((:+:))
import HAppS.State
import HAppS.Data
import GHC.Conc (STM)
+import System.Random (randomRIO)
+import Data.Digest.Pure.SHA (sha512, showDigest)
+import qualified Data.ByteString.Lazy.UTF8 as L (fromString)
-- | Data structure for information read from config file.
data Config = Config {
@@ -43,7 +47,6 @@ data Config = Config {
tableOfContents :: Bool, -- should each page have an automatic table of contents?
maxUploadSize :: Integer, -- maximum size of pages and file uploads
portNumber :: Int, -- port number to serve content on
- passwordSalt :: String, -- text to serve as salt in encrypting passwords
debugMode :: Bool, -- should debug info be printed to the console?
frontPage :: String, -- the front page of the wiki
noEdit :: [String], -- pages that cannot be edited through the web interface
@@ -62,7 +65,6 @@ defaultConfig = Config {
tableOfContents = True,
maxUploadSize = 100000,
portNumber = 5001,
- passwordSalt = "l91snthoae8eou2340987",
debugMode = False,
frontPage = "Front Page",
noEdit = ["Help"],
@@ -79,9 +81,13 @@ data SessionData = SessionData {
data Sessions a = Sessions {unsession::M.Map SessionKey a}
deriving (Read,Show,Eq,Typeable,Data)
+-- Password salt hashedPassword
+data Password = Password { pSalt :: String, pHashed :: String }
+ deriving (Read,Show,Eq,Typeable,Data)
+
data User = User {
uUsername :: String,
- uPassword :: String, -- password stored as SHA512 hash
+ uPassword :: Password,
uEmail :: String
} deriving (Show,Read,Typeable,Data)
@@ -101,7 +107,9 @@ $(deriveSerialize ''Config)
instance Version AppState
instance Version User
+instance Version Password
+$(deriveSerialize ''Password)
$(deriveSerialize ''User)
$(deriveSerialize ''AppState)
@@ -127,6 +135,23 @@ modSessions f = modify $ \s -> s {sessions = f $ sessions s}
isUser :: MonadReader AppState m => String -> m Bool
isUser name = liftM (M.member name) askUsers
+mkUser :: String -- username
+ -> String -- email
+ -> String -- unhashed password
+ -> IO User
+mkUser uname email pass = do
+ salt <- genSalt
+ return $ User { uUsername = uname,
+ uPassword = Password { pSalt = salt, pHashed = hashPassword salt pass },
+ uEmail = email }
+
+genSalt :: IO String
+genSalt =
+ replicateM 32 $ randomRIO ('0','z')
+
+hashPassword :: String -> String -> String
+hashPassword salt pass = showDigest $ sha512 $ L.fromString $ salt ++ pass
+
addUser :: MonadState AppState m => String -> User -> m ()
addUser name u = modUsers $ M.insert name u
@@ -137,7 +162,10 @@ authUser :: MonadReader AppState m => String -> String -> m Bool
authUser name pass = do
users' <- askUsers
case M.lookup name users' of
- Just u -> return $ pass == uPassword u
+ Just u -> do
+ let salt = pSalt $ uPassword u
+ let hashed = pHashed $ uPassword u
+ return $ hashed == hashPassword salt pass
Nothing -> return False
listUsers :: MonadReader AppState m => m [String]
View
5 README.markdown
@@ -100,7 +100,6 @@ option `-f [filename]`. A configuration file takes the following form:
tableOfContents = False,
maxUploadSize = 100000,
portNumber = 5001,
- passwordSalt = "l91snthoae8eou2340987",
debugMode = True,
frontPage = "Front Page",
noEdit = ["Help", "Front Page"],
@@ -133,10 +132,6 @@ option `-f [filename]`. A configuration file takes the following form:
- `portNumber` is the number of the port on which the wiki will be served.
-- `passwordSalt` is used to hash the passwords in the user database;
- you should change this to something unique if you use gitit in a
- production setting.
-
- `debugMode` is either `True` or `False`. If it is `True`, debug information
will be printed to the console when gitit is running.
View
1 data/SampleConfig.hs
@@ -6,7 +6,6 @@ staticDir = "static",
tableOfContents = False,
maxUploadSize = 100000,
portNumber = 5001,
-passwordSalt = "l91snthoae8eou2340987",
debugMode = True,
frontPage = "Front Page",
noEdit = ["Help", "Front Page"],
View
4 gitit.cabal
@@ -1,5 +1,5 @@
name: gitit
-version: 0.3.4.2
+version: 0.4
Cabal-version: >= 1.2
build-type: Simple
synopsis: Wiki using HAppS, git, and pandoc.
@@ -50,7 +50,7 @@ Executable gitit
utf8-string, HAppS-Server >= 0.9.3 && < 0.9.4,
HAppS-State >= 0.9.3 && < 0.9.4,
HAppS-Data >= 0.9.3 && < 0.9.4, SHA > 1, HTTP,
- HStringTemplate
+ HStringTemplate, random
if impl(ghc >= 6.10)
build-depends: base >= 4, syb
ghc-options: -Wall -threaded

0 comments on commit 5a0bee3

Please sign in to comment.
Something went wrong with that request. Please try again.