วิธีป้องกันไม่ให้เบราว์เซอร์เรียกใช้ป๊อปอัปการตรวจสอบสิทธิ์พื้นฐานและจัดการข้อผิดพลาด 401 โดยใช้ Jquery


107

ฉันต้องการส่งคำขออนุญาตโดยใช้การตรวจสอบสิทธิ์พื้นฐาน ฉันติดตั้งสำเร็จโดยใช้ jquery อย่างไรก็ตามเมื่อฉันได้รับข้อผิดพลาด 401 ป๊อปอัพเบราว์เซอร์การตรวจสอบสิทธิ์ขั้นพื้นฐานถูกเปิดขึ้นและไม่เรียกการเรียกกลับข้อผิดพลาด jquery ajax


คำตอบ:


46

เมื่อเร็ว ๆ นี้ฉันก็ประสบปัญหานี้เช่นกัน เนื่องจากคุณไม่สามารถเปลี่ยนลักษณะการทำงานเริ่มต้นของเบราว์เซอร์ในการแสดงป๊อปอัปในกรณีของ401( การตรวจสอบสิทธิ์ขั้นพื้นฐานหรือการย่อย ) มีสองวิธีในการแก้ไขปัญหานี้:

  • เปลี่ยนการตอบสนองของเซิร์ฟเวอร์เป็นไม่ส่งคืนไฟล์401. ส่งคืน200รหัสแทนและจัดการสิ่งนี้ในไคลเอนต์ jQuery ของคุณ
  • เปลี่ยนวิธีการที่คุณใช้ในการอนุญาตเป็นค่าที่กำหนดเองในส่วนหัวของคุณ เบราว์เซอร์จะแสดงป๊อปอัพสำหรับขั้นพื้นฐานและDigest คุณต้องเปลี่ยนแปลงสิ่งนี้ทั้งในไคลเอนต์และเซิร์ฟเวอร์

    headers : {
      "Authorization" : "BasicCustom"
    }

โปรดดูที่นี้สำหรับตัวอย่างของการใช้ jQuery กับตรวจสอบสิทธิ์พื้นฐาน


10
WWW-Authenticate: xBasic realm = com.example สามารถทำได้ร่วมกับรหัสสถานะคลาสสิก 401 โพสต์บล็อกนี้แสดงคำใบ้ให้ฉัน (ฉันไม่ใช่เจ้าของบล็อก) loudvchar.blogspot.ca/2010/11/…
น.

2
@PM คำตอบของบล็อกเป็นทางออกที่สมบูรณ์แบบ โปรดทราบว่าหากใช้<security:http-basic/>คุณไม่จำเป็นต้องกำหนดbasicAuthenticationFilterแต่ควรกำหนดเป็น<security:http-basic entry-point-ref="myBasicAuthenticationEntryPoint"/>ไฟล์.
Brett Ryan

คุณช่วยบอกวิธีลบล้างการตอบกลับก่อนส่งกลับไปยังลูกค้าได้ไหมฉันใช้ jaxrs ที่มีการตรวจสอบสิทธิ์ขั้นพื้นฐาน ฉันต้องลบล้างคลาสใดเพื่อแก้ไขการตอบสนอง
mohammed sameen

ด้วยเหตุผลบางอย่างฉันได้รับป๊อปอัปเมื่อส่งคืน a 401และ WWW-Authenticate:Bearer WWW-Authenticate:NTLM WWW-Authenticate:Negotiateคุณรู้ไหมว่าทำไมถึงเป็นเช่นนั้น
DevEng

34

ส่งคืนรหัสสถานะ 400 ทั่วไปจากนั้นประมวลผลฝั่งไคลเอ็นต์นั้น

หรือคุณสามารถเก็บ 401 ไว้และไม่ส่งคืนส่วนหัว WWW-Authenticate ซึ่งเป็นสิ่งที่เบราว์เซอร์ตอบสนองด้วยป๊อปอัปการตรวจสอบสิทธิ์ หากส่วนหัว WWW-Authenticate ขาดหายไปเบราว์เซอร์จะไม่แจ้งให้ป้อนข้อมูลรับรอง


6
@MortenHaraldsen การตอบสนอง 401 เป็นการตอบสนองที่เหมาะสมสำหรับโอกาสนี้ปัญหาคือเบราว์เซอร์จะจัดการสิ่งนี้โดยอัตโนมัติแทนที่จะปล่อยให้แอปจาวาสคริปต์จัดการ คุณสามารถยึดติดกับมาตรฐานโดยไม่ส่งคืนการตอบสนองที่เหมาะสมตามที่มาตรฐานแนะนำหรือคุณสามารถเลือกที่จะไม่ยึดติดกับมาตรฐานโดยส่งคืนรหัสตอบกลับที่แนะนำมาตรฐาน เลือกของคุณ :)
บราฮีม

ในแอปด่วนของฉันฉันแก้ไขสิ่งนี้ด้วยบรรทัดเดียว: res.removeHeader('www-authenticate'); // prevents browser from popping up a basic auth window.
gstroup

1
@Ibraheem ไม่สามารถระบุได้ดีกว่าตัวเอง มาตรฐานถูกสร้างขึ้นโดยคนที่นั่งคุยกันไม่จำเป็นต้องเป็นคนที่นั่งลงและเขียนโค้ด
user2867288

15

คุณสามารถระงับป๊อปอัปการตรวจสอบสิทธิ์ขั้นพื้นฐานด้วย url คำขอที่มีลักษณะดังนี้

https://username:password@example.com/admin/...

หากคุณได้รับข้อผิดพลาด 401 (ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง) จะได้รับการจัดการอย่างถูกต้องด้วยการเรียกกลับข้อผิดพลาด jquery อาจทำให้เกิดปัญหาด้านความปลอดภัย (ในกรณีของโปรโตคอล http แทนที่จะเป็น https) แต่ก็ใช้งานได้

UPD:การสนับสนุนโซลูชันนี้จะถูกลบออกใน Chrome 59


น่าอัศจรรย์ !!!! แก้ไขปัญหาของฉันเนื่องจากปัญหากำลังพยายาม 192.168.1.1 และเราเตอร์ยังคงขอ Auth
Nadav Lebovitch

หากคุณไปที่แท็บ "เครือข่าย" ภายใต้เครื่องมือสำหรับนักพัฒนาในเบราว์เซอร์ใด ๆ คุณจะสามารถอ่านชื่อผู้ใช้และรหัสผ่านเป็นข้อความธรรมดาได้ มันใช้งานได้
Bruno Finger

19
อย่าทำแบบนี้เลยตอนนี้คำขอล็อกบนเว็บเซิร์ฟเวอร์ของคุณมีค่าสำหรับฉันมากแล้ว .. คำสั่งผสมชื่อผู้ใช้และรหัสผ่านฟรีพร้อมรหัสตอบกลับ! ขอบคุณ
Remco

แต่จะมีคนป้องกันไม่ให้ดูชื่อผู้ใช้และรหัสผ่านได้อย่างไร
sdx11

3
วิธีการตรวจสอบนี้จะกลายเป็นค่าเสื่อมราคาและ Chrome จะลดลงการสนับสนุนสำหรับข้อมูลประจำตัวที่ฝังตัวเช่นhttps://user:pass@host/ใน M59 ประมาณเดือนมิถุนายน 2017 ดูโพสต์ chromestatus บล็อกนี้สำหรับข้อมูลเพิ่มเติม
Garywoo

13

ดังที่คนอื่น ๆ ชี้ให้เห็นวิธีเดียวที่จะเปลี่ยนพฤติกรรมของเบราว์เซอร์คือตรวจสอบให้แน่ใจว่าการตอบกลับไม่มีรหัสสถานะ 401 หรือถ้าไม่มีก็ไม่ต้องรวมWWW-Authenticate: Basicส่วนหัว เนื่องจากการเปลี่ยนรหัสสถานะไม่ได้มีความหมายและไม่เป็นที่ต้องการแนวทางที่ดีคือการลบWWW-Authenticateส่วนหัวออก หากคุณไม่สามารถหรือไม่ต้องการแก้ไขแอปพลิเคชันเว็บเซิร์ฟเวอร์ของคุณคุณสามารถให้บริการหรือพร็อกซีผ่าน Apache ได้ตลอดเวลา (หากคุณไม่ได้ใช้ Apache อยู่แล้ว)

นี่คือการกำหนดค่าสำหรับ Apache เพื่อเขียนการตอบสนองใหม่เพื่อลบ IFF ส่วนหัวที่รับรองความถูกต้อง WWW ที่คำขอมีส่วนหัวX-Requested-With: XMLHttpRequest(ซึ่งกำหนดโดยค่าเริ่มต้นโดยเฟรมเวิร์ก Javascript ที่สำคัญเช่น JQuery / AngularJS ฯลฯ ... ) และการตอบกลับประกอบด้วย WWW-Authenticate: Basicส่วนหัว

ทดสอบกับ Apache 2.4 (ไม่แน่ใจว่าใช้ได้กับ 2.2 หรือไม่) ขึ้นอยู่กับmod_headersโมดูลที่กำลังติดตั้ง (บน Debian / Ubuntu sudo a2enmod headersและรีสตาร์ท Apache)

    <Location />
            # Make sure that if it is an XHR request,
            # we don't send back basic authentication header.
            # This is to prevent the browser from displaying a basic auth login dialog.
            Header unset WWW-Authenticate "expr=req('X-Requested-With') == 'XMLHttpRequest' && resp('WWW-Authenticate') =~ /^Basic/"
    </Location>   

1
หากต้องการทำเช่นเดียวกันกับ Nginx ให้ตั้งค่าproxy_hide_header WWW-Authenticate;
Cuga

7

ใช้ X-Requested-With: XMLHttpRequest กับส่วนหัวคำขอของคุณ ดังนั้นส่วนหัวของการตอบกลับจะไม่มี WWW-Authenticate: Basic

beforeSend: function (xhr) {
                    xhr.setRequestHeader('Authorization', ("Basic "
                        .concat(btoa(key))));
                    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
                },

1
นั่นไม่มีผลสำหรับฉัน คุณใช้เซิร์ฟเวอร์ประเภทใดที่ WWW-Authenticate จะไม่ถูกส่งรวมเมื่อคุณตั้งค่า XMLHttpRequest
Robert Antonucci

@RobertAntonucci มันคือ apache tomcat
sedhu

5

หากคุณใช้เซิร์ฟเวอร์ IIS คุณสามารถตั้งค่า IIS URL Rewriting (v2) เพื่อเขียนWWW-Authenticationส่วนหัวใหม่Noneใน URL ที่ร้องขอ

ให้คำแนะนำที่นี่

response_www_authenticateค่าที่คุณต้องการที่จะเปลี่ยนแปลง

หากคุณต้องการข้อมูลเพิ่มเติมเพิ่มความคิดเห็นแล้วฉันจะโพสต์ไฟล์ web.config


1
สิ่งนี้ได้ผลดี ฉันต้องการทราบว่าส่วน "response" ต้องเขียนเป็น "RESPONSE_www_authenticate" ใน URL Rewrite v2 บน IIS 7.5
Michael Freeman

3

หากส่วนหัว WWW-Authenticate ถูกลบออกคุณจะไม่ได้รับการแคชข้อมูลรับรองและจะไม่ได้รับส่วนหัวการอนุญาตกลับมาในคำขอ นั่นหมายความว่าตอนนี้คุณจะต้องป้อนข้อมูลรับรองสำหรับทุกคำขอใหม่ที่คุณสร้างขึ้น


นี่เป็นสิ่งที่สำคัญมากโดยเฉพาะอย่างยิ่ง
Tez Wingfield

2

หรือหากคุณสามารถปรับแต่งการตอบสนองของเซิร์ฟเวอร์คุณสามารถส่งคืน 403 Forbidden

เบราว์เซอร์จะไม่เปิดป๊อปอัปการรับรองความถูกต้องและการเรียกกลับ jquery จะถูกเรียก


5
ซึ่งขัดกับข้อกำหนด HTTP 1.1 โดยระบุว่า "... การอนุญาตจะไม่ช่วยและไม่ควรขอซ้ำ"
Jukka Dahlbom

1
สามารถรับ 403 ได้ในขณะที่ตรวจสอบสิทธิ์สำหรับทรัพยากรที่คุณไม่ได้รับอนุญาตให้เข้าถึง 401 ควรถูกส่งไปในที่ที่คุณยังไม่ได้รับการพิสูจน์ตัวตน
Brett Ryan

1

ใน Safari คุณสามารถใช้คำขอแบบซิงโครนัสเพื่อหลีกเลี่ยงไม่ให้เบราว์เซอร์แสดงป๊อปอัป แน่นอนว่าควรใช้คำขอแบบซิงโครนัสในกรณีนี้เพื่อตรวจสอบข้อมูลรับรองของผู้ใช้เท่านั้น ... คุณสามารถใช้คำขอดังกล่าวก่อนที่จะส่งคำขอจริงซึ่งอาจทำให้ผู้ใช้ได้รับประสบการณ์ที่ไม่ดีหากเนื้อหา (ที่ส่งหรือรับ) ค่อนข้างหนัก

    var xmlhttp=new XMLHttpRequest;
    xmlhttp.withCredentials=true;
    xmlhttp.open("POST",<YOUR UR>,false,username,password);
    xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
    xmlhttp.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

ในบริบทอื่น ๆ การใช้ "OPTIONS" แทน "POST" อาจช่วยได้เช่นกัน
Emmanuel Sellier

0

สร้าง / เข้าสู่ระบบ URL แทนที่จะยอมรับพารามิเตอร์ "ผู้ใช้" และ "รหัสผ่าน" ผ่าน GET และไม่ต้องการการตรวจสอบสิทธิ์ขั้นพื้นฐาน ที่นี่ใช้ php, node, java, อะไรก็ได้และแยกวิเคราะห์ไฟล์ passwd ของคุณและจับคู่พารามิเตอร์ (user / pass) กับมัน หากมีรายการที่ตรงกันให้เปลี่ยนเส้นทางไปที่http: // user: pass@domain.com/ (สิ่งนี้จะตั้งค่าข้อมูลรับรองในเบราว์เซอร์ของคุณ) หากไม่มีให้ส่งการตอบกลับ 401 (โดยไม่มีส่วนหัว WWW-Authenticate)


นั่นจะยอดเยี่ยมมากสำหรับใครก็ตามที่พยายามดมชื่อผู้ใช้ / รหัสผ่านจากคนข้อความธรรมดาในการโจมตีกลาง!
Ajax

0

จากด้านหลังด้วย Spring Boot ฉันใช้ BasicAuthenticationEntryPoint แบบกำหนดเอง:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.cors().and().authorizeRequests()
            ...
            .antMatchers(PUBLIC_AUTH).permitAll()
            .and().httpBasic()
//    https://www.baeldung.com/spring-security-basic-authentication
            .authenticationEntryPoint(authBasicAuthenticationEntryPoint())
            ...

@Bean
public BasicAuthenticationEntryPoint authBasicAuthenticationEntryPoint() {
    return new BasicAuthenticationEntryPoint() {
        {
            setRealmName("pirsApp");
        }

        @Override
        public void commence
                (HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx)
                throws IOException, ServletException {
            if (request.getRequestURI().equals(PUBLIC_AUTH)) {
                response.sendError(HttpStatus.PRECONDITION_FAILED.value(), "Wrong credentials");
            } else {
                super.commence(request, response, authEx);
            }
        }
    };
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.