2

When running my script, I am getting errors like this when i call header(). I know that there are already answers(How to fix "Headers already sent" error in PHP) BUT i dont understand and after trying all solutions only the java solution works and it's a very bad solution. Without the gCaptcha part (with only the form) my code is working and without the form (with only gCaptcha) my code is working too BUT if combined the code doesnt work...

My errors are like this for each header() call :
"Warning: Cannot modify header information - headers already sent by (output started at C:\wamp64\www\Travail\WebApp1\login.php:126) in C:\wamp64\www\Travail\WebApp1\login.php on line 251"

What do i need to change ? Honestly i have read all questions about this issue and after 8 hours i am still unable to fix the problem with a real solution.

My whole code:

<?php 
    ini_set('display_errors', 1);
    ini_set('display_startup_errors', 1);
    error_reporting(E_ALL);
    session_start();
    require_once "config.php";
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous">
    <title>Login</title>
    
</head>
<body>

        <nav class="navbar navbar-dark bg-primary">
            <div class="container-fluid">
                <a class="navbar-brand" href="index.php">My Web App</a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
                </button>
                <div class="collapse navbar-collapse" id="navbarNav">
                <ul class="navbar-nav">
                    <li class="nav-item">
                    <a class="nav-link active" aria-current="page" href="#">Home</a>
                    </li>
                    <li class="nav-item">
                    <a class="nav-link" href="signup.php">Sign Up</a>
                    </li>
                    <li class="nav-item">
                    <a class="nav-link" href="login.php">Login</a>
                    </li>
                    <li class="nav-item">
                    <a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Admin</a>
                    </li>
                </ul>
                </div>
            </div>
        </nav>



    <div class="login-form">
        <?php 
            if(isset($_GET['login_err']))
            {
                $err = htmlspecialchars($_GET['login_err']);
                
                switch($err)
                {
                    case 'password':
                    ?>
                        <div class="alert alert-danger">
                            <strong>Error</strong> wrong password
                        </div>
                    <?php
                    break;

                    case 'email':
                    ?>
                        <div class="alert alert-danger">
                            <strong>Error</strong> invalid email
                        </div>
                    <?php
                    break;

                    case 'already':
                    ?>
                        <div class="alert alert-danger">
                            <strong>Error</strong>Please enter valide information
                        </div>
                    <?php 

                }
            }
            ?>
        <form method="post" id="demo-form">
                    <h2 class="text-center">Login</h2>
                    <input type="hidden" id="g-token" name="g-token" />       
                    <div class="form-group">
                        <input type="email" name="email" class="form-control" placeholder="Email" required="required" autocomplete="on">
                    </div>
                    <div class="form-group">
                        <input type="password" name="password" class="form-control" placeholder="password" required="required" autocomplete="off">
                    </div>
                    <div class="form-group">
                        <button type="submit" name="btnSubmit" class=" btn btn-primary btn-block" style=" margin-left: 35% ;"  >Login</button>
                    </div>
                    <div class="form-group">
                        <a href="forgot.php" style="margin-left:25%;"> Forgot password ? </a>
                    </div>
        </form>
    </div>
    
    <style>
            .login-form {
                width: 340px;
                margin: 50px auto;
            }
            .login-form form {
                margin-bottom: 15px;
                background: #f7f7f7;
                box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
                padding: 30px;
            }

            .form-group {
                margin-top: 1em;;
            }


            .login-form h2 {
                margin: 0 0 15px;
            }
            .form-control, .btn {
                min-height: 38px;
                border-radius: 2px;
            }
            .btn {        
                font-size: 15px;
                font-weight: bold;
            }
    </style>

    <!-- Google Captcha Scripts -->
    <script src="https://www.google.com/recaptcha/api.js?render=6LcOd1cbAAAAAOiqthb4pwaH0vfrVDyB89D6q4V8"></script>
    
    <script>
        grecaptcha.ready(function() {
            grecaptcha.execute('6LcOd1cbAAAAAOiqthb4pwaH0vfrVDyB89D6q4V8', {action: 'homepage'}).then(function(token) {
                console.log(token);
            document.getElementById("g-token").value = token;
            });
        });
    </script>

    <!-- Optional JavaScript -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-gtEjrD/SeCtmISkJkNUaaKMoLD0//ElJ19smozuHV6z3Iehds+3Ulb9Bn9Plx0x4" crossorigin="anonymous"></script>

</body>
</html>


<?php 

        if(!empty($_POST["g-token"]) && !empty($_POST['email']) && !empty($_POST['password']))
        {

            

            $secretKey  = '6LcOd1cbAAAAALaDm52utDpT2bt6DNtMNNivW-9s';
            $token      = $_POST["g-token"];
            $ip         = $_SERVER['REMOTE_ADDR'];
        
            /* ======================= POST METHOD =====================*/ 
            $url = "https://www.google.com/recaptcha/api/siteverify?";
            $data = array('secret' => $secretKey, 'response' => $token, 'remoteip'=> $ip);
        
            // use key 'http' even if you send the request to https://...
            $options = array('http' => array(
                'method'  => 'POST',
                'content' => http_build_query($data),
                'header' => 'Content-Type: application/x-www-form-urlencoded'
            ));
            $context  = stream_context_create($options);
            $result = file_get_contents($url, false, $context);
            $response = json_decode($result);
            if($response->success)
            {
                echo '<center><h1>Validation Success!</h1></center>';
            }
            else
            {
                echo '<center><h1>Captcha Validation Failed..!</h1></center>';
            }

            //Avoid XSS
            $email = htmlspecialchars($_POST['email']);
            $password = htmlspecialchars(($_POST['password']));

            $email = strtolower($email);

            echo $email;
            echo $password;

            $check = $bdd->prepare('SELECT * FROM users WHERE email = ?');
            $check->execute(array($email));
            $data = $check->fetch();
            $row = $check->rowCount();

            echo $row;
            if($row > 0)
            {
                
                if(filter_var($email, FILTER_VALIDATE_EMAIL))
                {

                    if(password_verify($password, $data['password']))
                    {
                        $_SESSION['user'] = $data['token'];
                        //echo "<script type='text/javascript'>window.top.location='app.php';</script>"; exit;
                        header('Location: index.php');
                        die();
                    }else{echo "<script type='text/javascript'>window.top.location='login.php?login_err=password';</script>"; exit;
                        //header('Location: login.php?login_err=password'); die(); 
                    }
                }else{echo "<script type='text/javascript'>window.top.location='login.php?login_err=email';</script>"; exit; }
            }else{ echo "<script type='text/javascript'>window.top.location='login.php?login_err=already';</script>"; exit; }
            

        }
        else{  die();}

?>

If I replace "header('Location: index.php');" BY "echo "window.top.location='app.php';"; exit;" it works BUT it's not a real solution.

And by the way are my first 3 lines of code correct in order to show errors ? (I am using localhost wamp to run the code)

KrepaFR
  • 31
  • 3

1 Answers1

1

The header() function must be called before any other output is sent to the page. So in your case you would want to move the form processing block of PHP code at the bottom of your page to the top before you output the HTML. Note that this means you will also want to assign those validation response strings to variables and echo them further down in your page where appropriate.

P. Tisa
  • 146
  • 4
  • Ok I have tried what you said but when i put my form processing php before the i have a blank page. If i put the php between the "html nav part" and the "html form part" i get only the navbar. I think that php is causing my code to end/crash but i dont know why (btw i dont see any errors messages despite activating them). – KrepaFR Jun 26 '21 at 09:18
  • The if...else statement you are using for the form processing uses die() if $_POST is empty. Just remove that and the page will display the form HTML if $_POST is empty. I.e. by using die() you are preventing the rest of the page from being rendered when the form has not been submitted. – P. Tisa Jun 26 '21 at 16:16
  • Thanks it's working now. I forgot about that ^^ – KrepaFR Jun 28 '21 at 07:42