Skip to content

Commit

Permalink
Changed format of user file for more security.
Browse files Browse the repository at this point in the history
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
jgm committed Dec 31, 2008
1 parent 8ece19f commit 5a0bee3
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 20 deletions.
10 changes: 3 additions & 7 deletions Gitit.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
38 changes: 33 additions & 5 deletions Gitit/State.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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 {
Expand All @@ -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
Expand All @@ -62,7 +65,6 @@ defaultConfig = Config {
tableOfContents = True,
maxUploadSize = 100000,
portNumber = 5001,
passwordSalt = "l91snthoae8eou2340987",
debugMode = False,
frontPage = "Front Page",
noEdit = ["Help"],
Expand All @@ -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)

Expand All @@ -101,7 +107,9 @@ $(deriveSerialize ''Config)

instance Version AppState
instance Version User
instance Version Password

$(deriveSerialize ''Password)
$(deriveSerialize ''User)
$(deriveSerialize ''AppState)

Expand All @@ -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

Expand All @@ -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]
Expand Down
5 changes: 0 additions & 5 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
Expand Down Expand Up @@ -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.

Expand Down
1 change: 0 additions & 1 deletion data/SampleConfig.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ staticDir = "static",
tableOfContents = False,
maxUploadSize = 100000,
portNumber = 5001,
passwordSalt = "l91snthoae8eou2340987",
debugMode = True,
frontPage = "Front Page",
noEdit = ["Help", "Front Page"],
Expand Down
4 changes: 2 additions & 2 deletions gitit.cabal
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 5a0bee3

Please sign in to comment.